IdentifiableTypeImpl throws IllegalArgumentException with EmbeddedId and relations - bugfix + unit test (#1355)

* IdentifiableTypeImpl throws IllegalArgumentException with EmbeddedId and relations - bugfix + unit test

Because there are four new objects (java classes) in org.eclipse.persistence.testing.models.jpa.metamodel package MetamodelMetamodelTest.METAMODEL_ALL_TYPES is increased into 56.
These four objects have eights new attributes. This is why MetamodelMetamodelTest.METAMODEL_ALL_ATTRIBUTES_SIZE is increased into 158.

fixes #1228

Signed-off-by: Radek Felcman <radek.felcman@oracle.com>
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/ExceptionLocalizationResource.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/ExceptionLocalizationResource.java
index 2aa7455..d293928 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/ExceptionLocalizationResource.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/ExceptionLocalizationResource.java
@@ -148,7 +148,7 @@
                                            { "metamodel_managed_type_attribute_not_present", "The attribute [{0}] is not present in the managed type [{1}]." },
                                            { "metamodel_managed_type_attribute_type_incorrect", "Expected attribute type [{2}] on the existing attribute [{0}] on the managed type [{1}] but found attribute type [{3}]." },
                                            { "metamodel_identifiable_version_attribute_type_incorrect", "Expected version attribute type [{2}] on the existing version attribute [{0}] on the identifiable type [{1}] but found attribute type [{3}]." },
-                                           { "metamodel_identifiable_id_attribute_type_incorrect", "Expected id attribute type [{2}] on the existing id attribute [{0}] on the identifiable type [{1}] but found attribute type [{3}]." },
+                                           { "metamodel_identifiable_id_attribute_type_incorrect", "Expected id attribute type [{2}] in the existing id attributes [{0}] on the identifiable type [{1}] but found attribute types [{3}]." },
                                            { "metamodel_managed_type_declared_attribute_not_present_but_is_on_superclass", "The declared attribute [{0}] from the managed type [{1}] is not present - however, it is declared on a superclass." },
                                            { "metamodel_managed_type_attribute_return_type_incorrect", "Expected attribute return type [{2}] on the existing attribute [{0}] on the managed type [{1}] but found attribute return type [{3}]." },
                                            { "metamodel_incompatible_persistence_config_for_getIdType", "Incompatible persistence configuration getting Metamodel Id Type for the ManagedType [{0}]." },
diff --git a/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/models/jpa/metamodel/Bar.java b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/models/jpa/metamodel/Bar.java
new file mode 100644
index 0000000..be7fc6f
--- /dev/null
+++ b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/models/jpa/metamodel/Bar.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+//     Oracle - initial API and implementation from Oracle
+package org.eclipse.persistence.testing.models.jpa.metamodel;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name="CMP3_MM_BAR")
+public class Bar {
+
+	@Id
+	private Long id;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+}
diff --git a/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/models/jpa/metamodel/Foo.java b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/models/jpa/metamodel/Foo.java
new file mode 100644
index 0000000..926d5fa
--- /dev/null
+++ b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/models/jpa/metamodel/Foo.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+//     Oracle - initial API and implementation from Oracle
+package org.eclipse.persistence.testing.models.jpa.metamodel;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.Table;
+import java.util.List;
+
+@Entity
+@Table(name="CMP3_MM_FOO")
+public class Foo {
+
+	@Id
+	private Long id;
+
+	@OneToMany(mappedBy = "foo")
+	private List<FooBar> fooBars;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public List<FooBar> getFooBars() {
+		return fooBars;
+	}
+
+	public void setFooBars(List<FooBar> fooBars) {
+		this.fooBars = fooBars;
+	}
+
+}
diff --git a/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/models/jpa/metamodel/FooBar.java b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/models/jpa/metamodel/FooBar.java
new file mode 100644
index 0000000..e64f220
--- /dev/null
+++ b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/models/jpa/metamodel/FooBar.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+//     Oracle - initial API and implementation from Oracle
+package org.eclipse.persistence.testing.models.jpa.metamodel;
+
+import jakarta.persistence.EmbeddedId;
+import jakarta.persistence.Entity;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.MapsId;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name="CMP3_MM_FOOBAR")
+public class FooBar {
+
+	@EmbeddedId
+	private FooBarId id;
+
+	@ManyToOne
+	@JoinColumn(name = "bar_id")
+	@MapsId("barId")
+	private Bar bar;
+
+	@ManyToOne
+	@JoinColumn(name = "foo_id")
+	@MapsId("fooId")
+	private Foo foo;
+
+	public FooBarId getId() {
+		return id;
+	}
+
+	public void setId(FooBarId id) {
+		this.id = id;
+	}
+
+	public Bar getBar() {
+		return bar;
+	}
+
+	public void setBar(Bar bar) {
+		this.bar = bar;
+	}
+
+	public Foo getFoo() {
+		return foo;
+	}
+
+	public void setFoo(Foo foo) {
+		this.foo = foo;
+	}
+
+}
diff --git a/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/models/jpa/metamodel/FooBarId.java b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/models/jpa/metamodel/FooBarId.java
new file mode 100644
index 0000000..1d55506
--- /dev/null
+++ b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/models/jpa/metamodel/FooBarId.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+//     Oracle - initial API and implementation from Oracle
+package org.eclipse.persistence.testing.models.jpa.metamodel;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
+import java.io.Serializable;
+import java.util.Objects;
+
+@Embeddable
+public class FooBarId implements Serializable {
+
+	@Column(name = "foo_id")
+	private Long fooId;
+
+	@Column(name = "bar_id")
+	private Long barId;
+
+	public Long getFooId() {
+		return fooId;
+	}
+
+	public void setFooId(Long fooId) {
+		this.fooId = fooId;
+	}
+
+	public Long getBarId() {
+		return barId;
+	}
+
+	public void setBarId(Long barId) {
+		this.barId = barId;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if (this == o) return true;
+		if (o == null || getClass() != o.getClass()) return false;
+		FooBarId fooBarId = (FooBarId) o;
+		return fooId.equals(fooBarId.fooId) && barId.equals(fooBarId.barId);
+	}
+
+	@Override
+	public int hashCode() {
+		return Objects.hash(fooId, barId);
+	}
+}
diff --git a/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/tests/jpa/metamodel/MetamodelMetamodelTest.java b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/tests/jpa/metamodel/MetamodelMetamodelTest.java
index 57f60af..80e205e 100644
--- a/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/tests/jpa/metamodel/MetamodelMetamodelTest.java
+++ b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/tests/jpa/metamodel/MetamodelMetamodelTest.java
@@ -82,6 +82,8 @@
 import org.eclipse.persistence.testing.models.jpa.metamodel.EmbeddedPK;
 import org.eclipse.persistence.testing.models.jpa.metamodel.Enclosure;
 import org.eclipse.persistence.testing.models.jpa.metamodel.EnclosureIdClassPK;
+import org.eclipse.persistence.testing.models.jpa.metamodel.FooBar;
+import org.eclipse.persistence.testing.models.jpa.metamodel.FooBarId;
 import org.eclipse.persistence.testing.models.jpa.metamodel.GalacticPosition;
 import org.eclipse.persistence.testing.models.jpa.metamodel.HardwareDesigner;
 import org.eclipse.persistence.testing.models.jpa.metamodel.MSRootPropertyAccess;
@@ -125,9 +127,9 @@
  */
 public class MetamodelMetamodelTest extends MetamodelTest {
 
-    public static final int METAMODEL_ALL_ATTRIBUTES_SIZE = 150;//6;
+    public static final int METAMODEL_ALL_ATTRIBUTES_SIZE = 158;//6;
     // Note: Since BasicTypes are lazy - loaded into the metamodel-types Map - this test must preceed any test that verifies all BasicType objects like "testIdentifiableType_getIdType_Method"
-    public static final int METAMODEL_ALL_TYPES = 52;
+    public static final int METAMODEL_ALL_TYPES = 56;
     public static final int METAMODEL_MANUFACTURER_DECLARED_TYPES = 28;
     // Get # of processor cores (hard cores + hyperthreaded cores)
     public static final int numberProcessingUnits = Runtime.getRuntime().availableProcessors();
@@ -202,6 +204,7 @@
             suite.addTest(new MetamodelMetamodelTest("testIdentifiableType_getVersion_Method"));
             suite.addTest(new MetamodelMetamodelTest("testIdentifiableType_getDeclaredId_Method"));
             suite.addTest(new MetamodelMetamodelTest("testIdentifiableType_getId_Method"));
+            suite.addTest(new MetamodelMetamodelTest("testIdentifiableType_getId_with_EmbeddedId_Method"));
             suite.addTest(new MetamodelMetamodelTest("testIdentifiableType_getDeclaredId_normal_execution_attribute_is_declared"));
             suite.addTest(new MetamodelMetamodelTest("testIdentifiableType_getDeclaredId_variant_execution_attribute_is_declared_above"));
             suite.addTest(new MetamodelMetamodelTest("testIdentifiableType_getDeclaredId_variant_execution_attribute_is_not_declared_at_all"));
@@ -1700,6 +1703,42 @@
         }
     }
 
+    public void testIdentifiableType_getId_with_EmbeddedId_Method() {
+        EntityManager em = null;
+        boolean expectedIAExceptionThrown = false;
+        try {
+            em = privateTestSetup();
+            assertNotNull(em);
+            Metamodel metamodel = em.getMetamodel();
+            assertNotNull(metamodel);
+            EntityType<FooBar> fooBarEntityType = metamodel.entity(FooBar.class);
+            assertNotNull(fooBarEntityType);
+            assertTrue(fooBarEntityType.hasSingleIdAttribute());
+
+            // Actual Test Case
+            /**
+             *  Return the attribute that corresponds to the id attribute of
+             *  the entity or mapped superclass.
+             *  @param type  the type of the represented id attribute
+             *  @return id attribute
+             *  @throws IllegalArgumentException if id attribute of the given
+             *          type is not present in the identifiable type or if
+             *          the identifiable type has an id class
+             */
+            //<Y> SingularAttribute<? super X, Y> getId(Class<Y> type);
+
+            SingularAttribute<? super FooBar, FooBarId> fooBarId = fooBarEntityType.getId(FooBarId.class);
+            //FooBarId declared by @EmbeddedId
+            assertNotNull(fooBarId);
+        } catch (IllegalArgumentException iae) {
+            iae.printStackTrace();
+            expectedIAExceptionThrown = true;
+        } finally {
+            cleanup(em);
+            assertFalse("An IAE exception should not occur here.", expectedIAExceptionThrown);
+        }
+    }
+
     public void testIdentifiableType_getVersion_Method() {
         EntityManager em = null;
         boolean expectedIAExceptionThrown = false;
diff --git a/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/tests/jpa/metamodel/MetamodelTableCreator.java b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/tests/jpa/metamodel/MetamodelTableCreator.java
index b1e0850..1005a62 100644
--- a/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/tests/jpa/metamodel/MetamodelTableCreator.java
+++ b/jpa/eclipselink.jpa.test/src/it/java/org/eclipse/persistence/testing/tests/jpa/metamodel/MetamodelTableCreator.java
@@ -98,6 +98,9 @@
         addTableDefinition(buildCMP3_MM_BOARD_SEQTable());
         addTableDefinition(buildCMP3_MM_PERSON_SEQTable());
         addTableDefinition(buildCMP3_MM_MANUF_CMP3_MM_MANUFTable());
+        addTableDefinition(buildCMP3_MM_BARTable());
+        addTableDefinition(buildCMP3_MM_FOOTable());
+        addTableDefinition(buildCMP3_MM_FOOBARTable());
     }
 
 
@@ -2355,5 +2358,65 @@
         return table;
     }
 
+    public static TableDefinition buildCMP3_MM_BARTable() {
+        TableDefinition table = new TableDefinition();
+        table.setName("CMP3_MM_BAR");
+
+        FieldDefinition field = new FieldDefinition();
+        field.setName("ID");
+        field.setTypeName("NUMERIC");
+        field.setSize(15);
+        field.setShouldAllowNull(false);
+        field.setIsPrimaryKey(true);
+        field.setUnique(false);
+        field.setIsIdentity(true);
+        table.addField(field);
+
+        return table;
+    }
+
+    public static TableDefinition buildCMP3_MM_FOOTable() {
+        TableDefinition table = new TableDefinition();
+        table.setName("CMP3_MM_FOO");
+
+        FieldDefinition field = new FieldDefinition();
+        field.setName("ID");
+        field.setTypeName("NUMERIC");
+        field.setSize(15);
+        field.setShouldAllowNull(false);
+        field.setIsPrimaryKey(true);
+        field.setUnique(false);
+        field.setIsIdentity(true);
+        table.addField(field);
+
+        return table;
+    }
+
+    public static TableDefinition buildCMP3_MM_FOOBARTable() {
+        TableDefinition table = new TableDefinition();
+        table.setName("CMP3_MM_FOOBAR");
+
+        FieldDefinition field = new FieldDefinition();
+        field.setName("bar_id");
+        field.setTypeName("NUMERIC");
+        field.setSize(15);
+        field.setShouldAllowNull(false);
+        field.setIsPrimaryKey(true);
+        field.setUnique(false);
+        field.setIsIdentity(true);
+        table.addField(field);
+
+        FieldDefinition field2 = new FieldDefinition();
+        field2.setName("foo_id");
+        field2.setTypeName("NUMERIC");
+        field2.setSize(15);
+        field2.setShouldAllowNull(false);
+        field2.setIsPrimaryKey(true);
+        field2.setUnique(false);
+        field2.setIsIdentity(true);
+        table.addField(field2);
+
+        return table;
+    }
 }
 
diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/metamodel/IdentifiableTypeImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/metamodel/IdentifiableTypeImpl.java
index 4d26e06..8e4d9fb 100644
--- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/metamodel/IdentifiableTypeImpl.java
+++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/metamodel/IdentifiableTypeImpl.java
@@ -26,6 +26,7 @@
 //     08/06/2010-2.2 mobrien 322018 - reduce protected instance variables to private to enforce encapsulation
 package org.eclipse.persistence.internal.jpa.metamodel;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -195,6 +196,8 @@
                     "metamodel_identifiable_id_attribute_is_incorrect_idclass",
                     new Object[] { this }));
         } else {
+            List<String> anAttributesMsg = new ArrayList<>();
+            List<String> anAttributesJavaTypeMsg = new ArrayList<>();
             // verify single id attribute type
             for(SingularAttribute<? super X, ?> anAttribute : idAttributes) {
                 // Verify type is correct - relax restriction on null and Object.class (from same classLoader)
@@ -202,11 +205,15 @@
                         type.getCanonicalName().equals(anAttribute.getJavaType().getCanonicalName())) {
                     idAttribute = (SingularAttribute<? super X, Y>) anAttribute;
                 } else {
-                    throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
-                        "metamodel_identifiable_id_attribute_type_incorrect",
-                        new Object[] { anAttribute, this, type, anAttribute.getJavaType() }));
+                    anAttributesMsg.add(anAttribute.toString());
+                    anAttributesJavaTypeMsg.add(anAttribute.getJavaType().toString());
                 }
             }
+            if (idAttribute == null) {
+                throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
+                        "metamodel_identifiable_id_attribute_type_incorrect",
+                        new Object[] { anAttributesMsg, this, type, anAttributesJavaTypeMsg }));
+            }
         }
         return idAttribute;
     }