diff --git a/examples/jpa-strict/pom.xml b/examples/jpa-strict/pom.xml new file mode 100644 index 000000000..08e308bdf --- /dev/null +++ b/examples/jpa-strict/pom.xml @@ -0,0 +1,173 @@ + + + + shamrock-examples-parent + org.jboss.shamrock + 1.0.0.Alpha1-SNAPSHOT + ../ + + 4.0.0 + + shamrock-strict-jpa-example + An example that only contains JPA related tests running in strict mode + + + org.jboss.shamrock + shamrock-jaxrs-deployment + provided + + + org.jboss.shamrock + shamrock-jpa-deployment + provided + + + org.postgresql + postgresql-protean + 42.2.5-SNAPSHOT + + + + + org.jboss.shamrock + shamrock-junit + test + + + org.jboss.shamrock + shamrock-graal + test + + + org.glassfish + javax.json + test + + + + + + + ${project.groupId} + shamrock-maven-plugin + ${project.version} + + + + build + + + + + + io.fabric8 + docker-maven-plugin + 0.26.1 + + + + postgres:10.5 + postgresql + + + hibernate_orm_test + hibernate_orm_test + hibernate_orm_test + + + 5432:5432 + + + + mapped + + 5432 + + + + + + + + + + + docker-start + compile + + stop + start + + + + docker-stop + post-integration-test + + stop + + + + + + + + + + native-image + + + !no-native + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + ${project.groupId} + shamrock-maven-plugin + ${project.version} + + + native-image + + native-image + + + false + true + true + + ${graalvmHome} + false + + + + + + + + + + + diff --git a/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/MainMethod.java b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/MainMethod.java new file mode 100644 index 000000000..f5d63cfc6 --- /dev/null +++ b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/MainMethod.java @@ -0,0 +1,9 @@ +package org.jboss.shamrock.example; + +public class MainMethod { + + public static void main(String... args) throws Exception { + Class.forName("org.jboss.shamrock.runtime.RuntimeRunner"); + } + +} diff --git a/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Address.java b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Address.java new file mode 100644 index 000000000..523980d99 --- /dev/null +++ b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Address.java @@ -0,0 +1,42 @@ +package org.jboss.shamrock.example.jpa; + +import javax.persistence.Embeddable; + +/** + * This is an enmarked @Embeddable class. + * Let's see if just being referenced by the main entity is enough to be detected. + * + * @author Emmanuel Bernard emmanuel@hibernate.org + */ +//FIXME : this used to be non-annotated explicitly for testing purposes +// added the annotation as it's illegal according to the ORM metadata validation +@Embeddable +public class Address { + private String street1; + private String street2; + private String zipCode; + + public String getStreet1() { + return street1; + } + + public void setStreet1(String street1) { + this.street1 = street1; + } + + public String getStreet2() { + return street2; + } + + public void setStreet2(String street2) { + this.street2 = street2; + } + + public String getZipCode() { + return zipCode; + } + + public void setZipCode(String zipCode) { + this.zipCode = zipCode; + } +} diff --git a/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Animal.java b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Animal.java new file mode 100644 index 000000000..405c85b0a --- /dev/null +++ b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Animal.java @@ -0,0 +1,16 @@ +package org.jboss.shamrock.example.jpa; + +/** + * @author Emmanuel Bernard emmanuel@hibernate.org + */ +public class Animal { + private double weight; + + public double getWeight() { + return weight; + } + + public void setWeight(double weight) { + this.weight = weight; + } +} diff --git a/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Customer.java b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Customer.java new file mode 100644 index 000000000..943d1e153 --- /dev/null +++ b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Customer.java @@ -0,0 +1,48 @@ +package org.jboss.shamrock.example.jpa; + +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.Id; + +/** + * Used to test reflection references for JPA + * + * @author Emmanuel Bernard emmanuel@hibernate.org + */ +@Entity +public class Customer extends Human { + @Id + // no getter explicitly to test field only reflective access + private Long id; + + private Address address; + private WorkAddress workAddress; + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + // Address is referenced but not marked as @Embeddable + @Embedded + public Address getAddress() { + return address; + } + + public WorkAddress getWorkAddress() { + return workAddress; + } + + public void setWorkAddress(WorkAddress workAddress) { + this.workAddress = workAddress; + } + + public void setAddress(Address address) { + this.address = address; + } +} diff --git a/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Human.java b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Human.java new file mode 100644 index 000000000..487af53d5 --- /dev/null +++ b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Human.java @@ -0,0 +1,21 @@ +package org.jboss.shamrock.example.jpa; + +import javax.persistence.MappedSuperclass; + +/** + * Mapped superclass test + * + * @author Emmanuel Bernard emmanuel@hibernate.org + */ +@MappedSuperclass +public class Human extends Animal { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/JPATestBootstrapEndpoint.java b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/JPATestBootstrapEndpoint.java new file mode 100644 index 000000000..e9f80dc6d --- /dev/null +++ b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/JPATestBootstrapEndpoint.java @@ -0,0 +1,103 @@ +package org.jboss.shamrock.example.jpa; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; +import java.util.UUID; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.EntityTransaction; +import javax.persistence.Persistence; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Various tests for the JPA integration. WARNING: these tests will ONLY pass in Substrate, as it also verifies + * reflection non-functionality. + */ +@WebServlet(name = "JPATestBootstrapEndpoint", urlPatterns = "/jpa/testbootstrap") +public class JPATestBootstrapEndpoint extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + try { + testStoreLoadOnJPA(); + } catch (Exception e) { + reportException("Oops, shit happened, No boot for you!", e, resp); + } + + resp.getWriter().write("OK"); + } + + public void testStoreLoadOnJPA() throws Exception { + EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("templatePU"); + System.out.println("Hibernate EntityManagerFactory: booted"); + + doStuffWithHibernate(entityManagerFactory); + + entityManagerFactory.close(); + System.out.println("Hibernate EntityManagerFactory: shut down"); + + } + + private static void doStuffWithHibernate(EntityManagerFactory entityManagerFactory) { + EntityManager em = entityManagerFactory.createEntityManager(); + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + persistNewPerson(em); + + listExistingPersons(em); + + transaction.commit(); + em.close(); + } + + private static void listExistingPersons(EntityManager em) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + + CriteriaQuery cq = cb.createQuery(Person.class); + Root from = cq.from(Person.class); + cq.select(from); + TypedQuery q = em.createQuery(cq); + List allpersons = q.getResultList(); + StringBuilder sb = new StringBuilder("list of stored Person names:\n\t"); + for (Person p : allpersons) { + p.describeFully(sb); + sb.append("\n\t"); + } + sb.append("\nList complete.\n"); + System.out.print(sb); + } + + private static void persistNewPerson(EntityManager entityManager) { + Person person = new Person(); + person.setName(randomName()); + person.setAddress(new SequencedAddress("Street " + randomName())); + entityManager.persist(person); + } + + private static String randomName() { + return UUID.randomUUID().toString(); + } + + private void reportException(String errorMessage, final Exception e, final HttpServletResponse resp) throws IOException { + final PrintWriter writer = resp.getWriter(); + if (errorMessage != null) { + writer.write(errorMessage); + writer.write(" "); + } + writer.write(e.toString()); + writer.append("\n\t"); + e.printStackTrace(writer); + writer.append("\n\t"); + } + +} diff --git a/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/JPATestReflectionEndpoint.java b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/JPATestReflectionEndpoint.java new file mode 100644 index 000000000..2ee3bd85d --- /dev/null +++ b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/JPATestReflectionEndpoint.java @@ -0,0 +1,138 @@ +package org.jboss.shamrock.example.jpa; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Various tests for the JPA integration. + * WARNING: these tests will ONLY pass in Substrate, as it also verifies reflection non-functionality. + */ +@WebServlet(name = "JPATestReflectionEndpoint", urlPatterns = "/jpa/testreflection") +public class JPATestReflectionEndpoint extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + makeSureNonEntityAreDCE(resp); + makeSureEntitiesAreAccessibleViaReflection(resp); + makeSureNonAnnotatedEmbeddableAreAccessibleViaReflection(resp); + makeSureAnnotatedEmbeddableAreAccessibleViaReflection(resp); + makeSureClassAreAccessibleViaReflection("org.jboss.shamrock.example.jpa.Human", "Unable to enlist @MappedSuperclass", resp); + makeSureClassAreAccessibleViaReflection("org.jboss.shamrock.example.jpa.Animal", "Unable to enlist entity superclass", resp); + resp.getWriter().write("OK"); + } + + private void makeSureClassAreAccessibleViaReflection(String className, String errorMessage, HttpServletResponse resp) throws IOException { + try { + className = getTrickedClassName(className); + + Class custClass = Class.forName(className); + Object instance = custClass.newInstance(); + } + catch (Exception e) { + reportException(errorMessage, e, resp); + } + } + + private void makeSureEntitiesAreAccessibleViaReflection(HttpServletResponse resp) throws IOException { + try { + String className = getTrickedClassName(org.jboss.shamrock.example.jpa.Customer.class.getName()); + + Class custClass = Class.forName(className); + Object instance = custClass.newInstance(); + Field id = custClass.getDeclaredField("id"); + id.setAccessible(true); + if (id.get(instance) != null) { + resp.getWriter().write("id should be reachable and null"); + } + Method setter = custClass.getDeclaredMethod("setName", String.class); + Method getter = custClass.getDeclaredMethod("getName"); + setter.invoke(instance, "Emmanuel"); + if (! "Emmanuel".equals(getter.invoke(instance))) { + resp.getWriter().write("getter / setter should be reachable and usable"); + } + } + catch (Exception e) { + reportException(e, resp); + } + } + + private void makeSureAnnotatedEmbeddableAreAccessibleViaReflection(HttpServletResponse resp) throws IOException { + try { + String className = getTrickedClassName(org.jboss.shamrock.example.jpa.WorkAddress.class.getName()); + + Class custClass = Class.forName(className); + Object instance = custClass.newInstance(); + Method setter = custClass.getDeclaredMethod("setCompany", String.class); + Method getter = custClass.getDeclaredMethod("getCompany"); + setter.invoke(instance, "Red Hat"); + if (! "Red Hat".equals(getter.invoke(instance))) { + resp.getWriter().write("@Embeddable embeddable should be reachable and usable"); + } + } + catch (Exception e) { + reportException(e, resp); + } + } + private void makeSureNonAnnotatedEmbeddableAreAccessibleViaReflection(HttpServletResponse resp) throws IOException { + try { + String className = getTrickedClassName(org.jboss.shamrock.example.jpa.Address.class.getName()); + + Class custClass = Class.forName(className); + Object instance = custClass.newInstance(); + Method setter = custClass.getDeclaredMethod("setStreet1", String.class); + Method getter = custClass.getDeclaredMethod("getStreet1"); + setter.invoke(instance, "1 rue du General Leclerc"); + if (! "1 rue du General Leclerc".equals(getter.invoke(instance))) { + resp.getWriter().write("Non @Embeddable embeddable getter / setter should be reachable and usable"); + } + } + catch (Exception e) { + reportException(e, resp); + } + } + + private void makeSureNonEntityAreDCE(HttpServletResponse resp) { + try { + String className = getTrickedClassName(org.jboss.shamrock.example.jpa.NotAnEntityNotReferenced.class.getName()); + + Class custClass = Class.forName(className); + resp.getWriter().write("Should not be able to find a non referenced non entity class"); + Object instance = custClass.newInstance(); + } + catch (Exception e) { + // Expected outcome + } + } + + /** + * Trick SubstrateVM not to detect a simple use of Class.forname + */ + private String getTrickedClassName(String className) { + className = className + " ITrickYou"; + className = className.subSequence(0, className.indexOf(' ')).toString(); + return className; + } + + private void reportException(final Exception e, final HttpServletResponse resp) throws IOException { + reportException(null, e, resp); + } + + private void reportException(String errorMessage, final Exception e, final HttpServletResponse resp) throws IOException { + final PrintWriter writer = resp.getWriter(); + if ( errorMessage != null ) { + writer.write(errorMessage); + writer.write(" "); + } + writer.write(e.toString()); + writer.append("\n\t"); + e.printStackTrace(writer); + writer.append("\n\t"); + } + +} diff --git a/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/NotAnEntityNotReferenced.java b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/NotAnEntityNotReferenced.java new file mode 100644 index 000000000..f918171bd --- /dev/null +++ b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/NotAnEntityNotReferenced.java @@ -0,0 +1,11 @@ +package org.jboss.shamrock.example.jpa; + +/** + * Should not be referenced by the code + * So be dead code eliminated. + * Used to test that entities get referenced but not non entities + * + * @author Emmanuel Bernard emmanuel@hibernate.org + */ +public class NotAnEntityNotReferenced { +} diff --git a/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Person.java b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Person.java new file mode 100644 index 000000000..3af490f65 --- /dev/null +++ b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/Person.java @@ -0,0 +1,58 @@ +package org.jboss.shamrock.example.jpa; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +@Entity +public class Person { + + private long id; + private String name; + private SequencedAddress address; + + public Person() { + } + + public Person(long id, String name, SequencedAddress address) { + this.id = id; + this.name = name; + this.address = address; + } + + @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="personSeq") + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + public SequencedAddress getAddress() { + return address; + } + + public void setAddress(SequencedAddress address) { + this.address = address; + } + + public void describeFully(StringBuilder sb) { + sb.append( "Person with id=" ).append( id ).append( ", name='" ).append( name ).append( "', address { " ); + getAddress().describeFully( sb ); + sb.append( " }" ); + } +} diff --git a/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/SequencedAddress.java b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/SequencedAddress.java new file mode 100644 index 000000000..101b263b0 --- /dev/null +++ b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/SequencedAddress.java @@ -0,0 +1,41 @@ +package org.jboss.shamrock.example.jpa; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class SequencedAddress { + + private long id; + private String street; + + public SequencedAddress() { + } + + public SequencedAddress(String street) { + this.street = street; + } + + @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="addressSeq") + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getStreet() { + return street; + } + + public void setStreet(String name) { + this.street = name; + } + + public void describeFully(StringBuilder sb) { + sb.append( "Address with id=" ).append( id ).append( ", street='" ).append( street ).append( "'" ); + } +} diff --git a/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/WorkAddress.java b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/WorkAddress.java new file mode 100644 index 000000000..2a2c37e0b --- /dev/null +++ b/examples/jpa-strict/src/main/java/org/jboss/shamrock/example/jpa/WorkAddress.java @@ -0,0 +1,21 @@ +package org.jboss.shamrock.example.jpa; + +import javax.persistence.Embeddable; + +/** + * Class marked @Embeddable explicitly so it is picked up. + * + * @author Emmanuel Bernard emmanuel@hibernate.org + */ +@Embeddable +public class WorkAddress { + private String company; + + public String getCompany() { + return company; + } + + public void setCompany(String company) { + this.company = company; + } +} diff --git a/examples/jpa-strict/src/main/resources/META-INF/persistence.xml b/examples/jpa-strict/src/main/resources/META-INF/persistence.xml new file mode 100644 index 000000000..7ba2efca3 --- /dev/null +++ b/examples/jpa-strict/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,37 @@ + + + + + Hibernate test case template Persistence Unit + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/test/JPABootstrapInGraalITCase.java b/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/test/JPABootstrapInGraalITCase.java new file mode 100644 index 000000000..185ebdfdb --- /dev/null +++ b/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/test/JPABootstrapInGraalITCase.java @@ -0,0 +1,23 @@ +package org.jboss.shamrock.example.test; + +import org.jboss.shamrock.example.testutils.URLTester; +import org.jboss.shamrock.junit.GraalTest; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; + +/** + * Test reflection around JPA entities + * + * @author Emmanuel Bernard emmanuel@hibernate.org + */ +@RunWith(GraalTest.class) +public class JPABootstrapInGraalITCase { + + @Test + public void testFieldAndGetterReflectionOnEntityFromServlet() throws Exception { + assertEquals("OK", URLTester.relative("jpa/testbootstrap").invokeURL().asString()); + } + +} diff --git a/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/test/JPAReflectionInGraalITCase.java b/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/test/JPAReflectionInGraalITCase.java new file mode 100644 index 000000000..9fbea6bea --- /dev/null +++ b/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/test/JPAReflectionInGraalITCase.java @@ -0,0 +1,23 @@ +package org.jboss.shamrock.example.test; + +import static org.junit.Assert.assertEquals; + +import org.jboss.shamrock.example.testutils.URLTester; +import org.jboss.shamrock.junit.GraalTest; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test reflection around JPA entities + * + * @author Emmanuel Bernard emmanuel@hibernate.org + */ +@RunWith(GraalTest.class) +public class JPAReflectionInGraalITCase { + + @Test + public void testFieldAndGetterReflectionOnEntityFromServlet() throws Exception { + assertEquals("OK", URLTester.relative("jpa/testreflection").invokeURL().asString()); + } + +} diff --git a/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/test/JPAStoreLoadTestCase.java b/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/test/JPAStoreLoadTestCase.java new file mode 100644 index 000000000..9b7d9b549 --- /dev/null +++ b/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/test/JPAStoreLoadTestCase.java @@ -0,0 +1,82 @@ +package org.jboss.shamrock.example.test; + +import java.util.List; +import java.util.UUID; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.EntityTransaction; +import javax.persistence.Persistence; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import org.jboss.shamrock.example.jpa.Person; +import org.jboss.shamrock.example.jpa.SequencedAddress; +import org.jboss.shamrock.junit.ShamrockTest; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * + */ +@RunWith(ShamrockTest.class) +public class JPAStoreLoadTestCase { + + @Test + public void testStoreLoadOnJPA() throws Exception { + EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory( "templatePU" ); + System.out.println( "Hibernate EntityManagerFactory: booted" ); + + doStuffWithHibernate( entityManagerFactory ); + + entityManagerFactory.close(); + System.out.println( "Hibernate EntityManagerFactory: shut down" ); + + } + + + private static void doStuffWithHibernate(EntityManagerFactory entityManagerFactory) { + EntityManager em = entityManagerFactory.createEntityManager(); + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + persistNewPerson( em ); + + listExistingPersons( em ); + + transaction.commit(); + em.close(); + } + + private static void listExistingPersons(EntityManager em) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + + CriteriaQuery cq = cb.createQuery( Person.class ); + Root from = cq.from( Person.class ); + cq.select( from ); + TypedQuery q = em.createQuery( cq ); + List allpersons = q.getResultList(); + StringBuilder sb = new StringBuilder( "list of stored Person names:\n\t" ); + for ( Person p : allpersons ) { + p.describeFully( sb ); + sb.append( "\n\t" ); + } + sb.append( "\nList complete.\n" ); + System.out.print( sb ); + } + + private static void persistNewPerson(EntityManager entityManager) { + Person person = new Person(); + person.setName( randomName() ); + person.setAddress( new SequencedAddress( "Street " + randomName() ) ); + entityManager.persist( person ); + } + + private static String randomName() { + return UUID.randomUUID().toString(); + } + + +} diff --git a/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/testutils/URLResponse.java b/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/testutils/URLResponse.java new file mode 100644 index 000000000..50a2ad39a --- /dev/null +++ b/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/testutils/URLResponse.java @@ -0,0 +1,14 @@ +package org.jboss.shamrock.example.testutils; + +import java.io.InputStream; + +import javax.json.JsonReader; + +public interface URLResponse { + + String asString(); + + InputStream asInputStream(); + + JsonReader asJsonReader(); +} diff --git a/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/testutils/URLTester.java b/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/testutils/URLTester.java new file mode 100644 index 000000000..b1b4cfaf0 --- /dev/null +++ b/examples/jpa-strict/src/test/java/org/jboss/shamrock/example/testutils/URLTester.java @@ -0,0 +1,84 @@ +package org.jboss.shamrock.example.testutils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; + +import javax.json.Json; +import javax.json.JsonReader; + +/** + * Convenience class to invoke an URL and return the response. + */ +public final class URLTester { + + private final URL fullURL; + + private URLTester(final URL fullURL) { + this.fullURL = fullURL; + } + + public static URLTester relative(final String relativePath) { + final URL uri; + try { + uri = new URL("http://localhost:8080/" + relativePath); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + return new URLTester(uri); + } + + private URLResponse privateInvokeURL() throws IOException { + URLConnection connection = fullURL.openConnection(); + InputStream in = connection.getInputStream(); + byte[] buf = new byte[100]; + int r; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + while ((r = in.read(buf)) > 0) { + out.write(buf, 0, r); + } + return new URLResponseAdapter(out); + } + + public URLResponse invokeURL() { + try { + return privateInvokeURL(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static class URLResponseAdapter implements URLResponse { + + private final ByteArrayOutputStream buffer; + + public URLResponseAdapter(final ByteArrayOutputStream buffer) { + this.buffer = buffer; + } + + @Override + public String toString() { + return new String(buffer.toByteArray()); + } + + @Override + public String asString() { + return toString(); + } + + @Override + public InputStream asInputStream() { + return new ByteArrayInputStream(buffer.toByteArray()); + } + + @Override + public JsonReader asJsonReader() { + return Json.createReader(asInputStream()); + } + } + +} diff --git a/examples/pom.xml b/examples/pom.xml index 4e8203b12..dbb13a6cf 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -16,6 +16,7 @@ permissive shared-library strict + jpa-strict diff --git a/examples/strict/pom.xml b/examples/strict/pom.xml index cc3bab693..f30dc6814 100644 --- a/examples/strict/pom.xml +++ b/examples/strict/pom.xml @@ -144,6 +144,55 @@ + + io.fabric8 + docker-maven-plugin + 0.26.1 + + + + postgres:10.5 + postgresql + + + hibernate_orm_test + hibernate_orm_test + hibernate_orm_test + + + 5432:5432 + + + + mapped + + 5432 + + + + + + + + + + + docker-start + compile + + stop + start + + + + docker-stop + post-integration-test + + stop + + + + diff --git a/examples/strict/src/main/java/org/jboss/shamrock/example/jpa/JPATestBootstrapEndpoint.java b/examples/strict/src/main/java/org/jboss/shamrock/example/jpa/JPATestBootstrapEndpoint.java new file mode 100644 index 000000000..789778c65 --- /dev/null +++ b/examples/strict/src/main/java/org/jboss/shamrock/example/jpa/JPATestBootstrapEndpoint.java @@ -0,0 +1,107 @@ +package org.jboss.shamrock.example.jpa; + +import javax.persistence.*; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; +import java.util.UUID; + +/** + * Various tests for the JPA integration. + * WARNING: these tests will ONLY pass in Substrate, as it also verifies reflection non-functionality. + */ +@WebServlet(name = "JPATestBootstrapEndpoint", urlPatterns = "/jpa/testbootstrap") +public class JPATestBootstrapEndpoint extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + try { + testStoreLoadOnJPA(); + } + catch (Exception e) { + reportException("Oops, shit happened, No boot for you!", e, resp); + } + + + resp.getWriter().write("OK"); + } + + + public void testStoreLoadOnJPA() throws Exception { + EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory( "templatePU" ); + System.out.println( "Hibernate EntityManagerFactory: booted" ); + + doStuffWithHibernate( entityManagerFactory ); + + entityManagerFactory.close(); + System.out.println( "Hibernate EntityManagerFactory: shut down" ); + + } + + private static void doStuffWithHibernate(EntityManagerFactory entityManagerFactory) { + EntityManager em = entityManagerFactory.createEntityManager(); + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + persistNewPerson( em ); + + listExistingPersons( em ); + + transaction.commit(); + em.close(); + } + + private static void listExistingPersons(EntityManager em) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + + CriteriaQuery cq = cb.createQuery( Person.class ); + Root from = cq.from( Person.class ); + cq.select( from ); + TypedQuery q = em.createQuery( cq ); + List allpersons = q.getResultList(); + StringBuilder sb = new StringBuilder( "list of stored Person names:\n\t" ); + for ( Person p : allpersons ) { + p.describeFully( sb ); + sb.append( "\n\t" ); + } + sb.append( "\nList complete.\n" ); + System.out.print( sb ); + } + + private static void persistNewPerson(EntityManager entityManager) { + Person person = new Person(); + person.setName( randomName() ); + person.setAddress( new SequencedAddress( "Street " + randomName() ) ); + entityManager.persist( person ); + } + + private static String randomName() { + return UUID.randomUUID().toString(); + } + + private void reportException(final Exception e, final HttpServletResponse resp) throws IOException { + reportException(null, e, resp); + } + + private void reportException(String errorMessage, final Exception e, final HttpServletResponse resp) throws IOException { + final PrintWriter writer = resp.getWriter(); + if ( errorMessage != null ) { + writer.write(errorMessage); + writer.write(" "); + } + writer.write(e.toString()); + writer.append("\n\t"); + e.printStackTrace(writer); + writer.append("\n\t"); + } + +} diff --git a/examples/strict/src/main/java/org/jboss/shamrock/example/jpa/Person.java b/examples/strict/src/main/java/org/jboss/shamrock/example/jpa/Person.java new file mode 100644 index 000000000..3af490f65 --- /dev/null +++ b/examples/strict/src/main/java/org/jboss/shamrock/example/jpa/Person.java @@ -0,0 +1,58 @@ +package org.jboss.shamrock.example.jpa; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +@Entity +public class Person { + + private long id; + private String name; + private SequencedAddress address; + + public Person() { + } + + public Person(long id, String name, SequencedAddress address) { + this.id = id; + this.name = name; + this.address = address; + } + + @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="personSeq") + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + public SequencedAddress getAddress() { + return address; + } + + public void setAddress(SequencedAddress address) { + this.address = address; + } + + public void describeFully(StringBuilder sb) { + sb.append( "Person with id=" ).append( id ).append( ", name='" ).append( name ).append( "', address { " ); + getAddress().describeFully( sb ); + sb.append( " }" ); + } +} diff --git a/examples/strict/src/main/java/org/jboss/shamrock/example/jpa/SequencedAddress.java b/examples/strict/src/main/java/org/jboss/shamrock/example/jpa/SequencedAddress.java new file mode 100644 index 000000000..101b263b0 --- /dev/null +++ b/examples/strict/src/main/java/org/jboss/shamrock/example/jpa/SequencedAddress.java @@ -0,0 +1,41 @@ +package org.jboss.shamrock.example.jpa; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class SequencedAddress { + + private long id; + private String street; + + public SequencedAddress() { + } + + public SequencedAddress(String street) { + this.street = street; + } + + @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="addressSeq") + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getStreet() { + return street; + } + + public void setStreet(String name) { + this.street = name; + } + + public void describeFully(StringBuilder sb) { + sb.append( "Address with id=" ).append( id ).append( ", street='" ).append( street ).append( "'" ); + } +} diff --git a/examples/strict/src/main/resources/META-INF/persistence.xml b/examples/strict/src/main/resources/META-INF/persistence.xml new file mode 100644 index 000000000..548b704a9 --- /dev/null +++ b/examples/strict/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,36 @@ + + + + + Hibernate test case template Persistence Unit + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/strict/src/test/java/org/jboss/shamrock/example/test/JPABootstrapInGraalITCase.java b/examples/strict/src/test/java/org/jboss/shamrock/example/test/JPABootstrapInGraalITCase.java new file mode 100644 index 000000000..185ebdfdb --- /dev/null +++ b/examples/strict/src/test/java/org/jboss/shamrock/example/test/JPABootstrapInGraalITCase.java @@ -0,0 +1,23 @@ +package org.jboss.shamrock.example.test; + +import org.jboss.shamrock.example.testutils.URLTester; +import org.jboss.shamrock.junit.GraalTest; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; + +/** + * Test reflection around JPA entities + * + * @author Emmanuel Bernard emmanuel@hibernate.org + */ +@RunWith(GraalTest.class) +public class JPABootstrapInGraalITCase { + + @Test + public void testFieldAndGetterReflectionOnEntityFromServlet() throws Exception { + assertEquals("OK", URLTester.relative("jpa/testbootstrap").invokeURL().asString()); + } + +} diff --git a/examples/strict/src/test/java/org/jboss/shamrock/example/test/JPAStoreLoadTestCase.java b/examples/strict/src/test/java/org/jboss/shamrock/example/test/JPAStoreLoadTestCase.java new file mode 100644 index 000000000..9b7d9b549 --- /dev/null +++ b/examples/strict/src/test/java/org/jboss/shamrock/example/test/JPAStoreLoadTestCase.java @@ -0,0 +1,82 @@ +package org.jboss.shamrock.example.test; + +import java.util.List; +import java.util.UUID; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.EntityTransaction; +import javax.persistence.Persistence; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import org.jboss.shamrock.example.jpa.Person; +import org.jboss.shamrock.example.jpa.SequencedAddress; +import org.jboss.shamrock.junit.ShamrockTest; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * + */ +@RunWith(ShamrockTest.class) +public class JPAStoreLoadTestCase { + + @Test + public void testStoreLoadOnJPA() throws Exception { + EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory( "templatePU" ); + System.out.println( "Hibernate EntityManagerFactory: booted" ); + + doStuffWithHibernate( entityManagerFactory ); + + entityManagerFactory.close(); + System.out.println( "Hibernate EntityManagerFactory: shut down" ); + + } + + + private static void doStuffWithHibernate(EntityManagerFactory entityManagerFactory) { + EntityManager em = entityManagerFactory.createEntityManager(); + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + persistNewPerson( em ); + + listExistingPersons( em ); + + transaction.commit(); + em.close(); + } + + private static void listExistingPersons(EntityManager em) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + + CriteriaQuery cq = cb.createQuery( Person.class ); + Root from = cq.from( Person.class ); + cq.select( from ); + TypedQuery q = em.createQuery( cq ); + List allpersons = q.getResultList(); + StringBuilder sb = new StringBuilder( "list of stored Person names:\n\t" ); + for ( Person p : allpersons ) { + p.describeFully( sb ); + sb.append( "\n\t" ); + } + sb.append( "\nList complete.\n" ); + System.out.print( sb ); + } + + private static void persistNewPerson(EntityManager entityManager) { + Person person = new Person(); + person.setName( randomName() ); + person.setAddress( new SequencedAddress( "Street " + randomName() ) ); + entityManager.persist( person ); + } + + private static String randomName() { + return UUID.randomUUID().toString(); + } + + +} diff --git a/jpa/deployment/src/main/java/org/jboss/shamrock/jpa/HibernateReflectiveNeeds.java b/jpa/deployment/src/main/java/org/jboss/shamrock/jpa/HibernateReflectiveNeeds.java index 5fa2825b3..8dac162fb 100644 --- a/jpa/deployment/src/main/java/org/jboss/shamrock/jpa/HibernateReflectiveNeeds.java +++ b/jpa/deployment/src/main/java/org/jboss/shamrock/jpa/HibernateReflectiveNeeds.java @@ -2,6 +2,7 @@ package org.jboss.shamrock.jpa; import java.util.Objects; +import org.hibernate.tuple.component.PojoComponentTuplizer; import org.jboss.shamrock.deployment.ProcessorContext; /** @@ -29,6 +30,7 @@ final class HibernateReflectiveNeeds { private void registerAll() { //Various well known needs: simpleConstructor(org.hibernate.tuple.entity.PojoEntityTuplizer.class); + allConstructors(org.hibernate.tuple.component.PojoComponentTuplizer.class); simpleConstructor(org.hibernate.persister.entity.SingleTableEntityPersister.class); simpleConstructor(org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl.class); simpleConstructor(org.hibernate.id.enhanced.SequenceStyleGenerator.class); @@ -53,6 +55,11 @@ final class HibernateReflectiveNeeds { simpleConstructor("org.postgresql.Driver"); } + private void allConstructors(final Class clazz) { + //FIXME simpleConstructor is not optimized yet to only enlist the no-arg constructor + simpleConstructor(clazz); + } + /** * Register classes which we know will only need to be created via their no-arg constructor * @param clazz diff --git a/jpa/deployment/src/main/java/org/jboss/shamrock/jpa/JpaJandexScavenger.java b/jpa/deployment/src/main/java/org/jboss/shamrock/jpa/JpaJandexScavenger.java index 0c4f14b91..59b4f27b2 100644 --- a/jpa/deployment/src/main/java/org/jboss/shamrock/jpa/JpaJandexScavenger.java +++ b/jpa/deployment/src/main/java/org/jboss/shamrock/jpa/JpaJandexScavenger.java @@ -64,6 +64,7 @@ final class JpaJandexScavenger { JPADeploymentTemplate template = context.getRecordingProxy(JPADeploymentTemplate.class); collector.dumpAllToJPATemplate(template); template.enlistPersistenceUnit(); + template.callHibernateFeatureInit(); } return collector; diff --git a/jpa/runtime/src/main/java/org/jboss/shamrock/jpa/JPADeploymentTemplate.java b/jpa/runtime/src/main/java/org/jboss/shamrock/jpa/JPADeploymentTemplate.java index f0e3adebc..59d8d6cb9 100644 --- a/jpa/runtime/src/main/java/org/jboss/shamrock/jpa/JPADeploymentTemplate.java +++ b/jpa/runtime/src/main/java/org/jboss/shamrock/jpa/JPADeploymentTemplate.java @@ -1,5 +1,7 @@ package org.jboss.shamrock.jpa; +import org.hibernate.protean.Hibernate; + import java.util.ArrayList; import java.util.List; @@ -16,4 +18,8 @@ public class JPADeploymentTemplate { public void enlistPersistenceUnit() { System.out.println("List of entities \n" + entities.toString()); } + + public void callHibernateFeatureInit() { + Hibernate.featureInit(); + } }