Skip to content

Commit

Permalink
[jpa] Add support for @embeddable and @Embedded
Browse files Browse the repository at this point in the history
  • Loading branch information
emmanuelbernard committed Aug 6, 2018
1 parent 989b4f0 commit c7e79a9
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.jboss.shamrock.example.jpa;

/**
* 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 [email protected]
*/
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;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
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 [email protected]
*/
@Entity
Expand All @@ -12,6 +15,9 @@ public class Customer {
// no getter explicitly to test field only reflective access
private Long id;

private Address address;
private WorkAddress workAddress;

private String name;

public String getName() {
Expand All @@ -21,4 +27,22 @@ public String getName() {
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class JPATestEndpoint extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
makeSureNonEntityAreDCE(resp);
makeSureEntitiesAreAccessibleViaReflection(resp);
makeSureNonAnnotatedEmbeddableAreAccessibleViaReflection(resp);
makeSureAnnotatedEmbeddableAreAccessibleViaReflection(resp);
resp.getWriter().write("OK");
}

Expand All @@ -41,6 +43,41 @@ private void makeSureEntitiesAreAccessibleViaReflection(HttpServletResponse resp
}
}

private void makeSureAnnotatedEmbeddableAreAccessibleViaReflection(HttpServletResponse resp) throws IOException {
try {
String className = getTrickedClassName("org.jboss.shamrock.example.jpa.WorkAddress");

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) {
resp.getWriter().write(e.toString());
}
}
private void makeSureNonAnnotatedEmbeddableAreAccessibleViaReflection(HttpServletResponse resp) throws IOException {
try {
String className = getTrickedClassName("org.jboss.shamrock.example.jpa.Address");

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) {
resp.getWriter().write(e.toString());
}
}

private void makeSureNonEntityAreDCE(HttpServletResponse resp) {
try {
String className = getTrickedClassName("org.jboss.shamrock.example.jpa.NotAnEntityNotReferenced");
Expand Down
Original file line number Diff line number Diff line change
@@ -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 [email protected]
*/
@Embeddable
public class WorkAddress {
private String company;

public String getCompany() {
return company;
}

public void setCompany(String company) {
this.company = company;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package org.jboss.shamrock.jpa;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.jboss.shamrock.deployment.ArchiveContext;
import org.jboss.shamrock.deployment.ProcessorContext;
import org.jboss.shamrock.deployment.ResourceProcessor;
import org.jboss.shamrock.deployment.codegen.BytecodeRecorder;

import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import java.util.Collection;

Expand All @@ -22,6 +25,8 @@
public class JPAAnnotationProcessor implements ResourceProcessor {

private static final DotName JPA_ENTITY = DotName.createSimple(Entity.class.getName());
private static final DotName EMBEDDABLE = DotName.createSimple(Embeddable.class.getName());
private static final DotName EMBEDDED = DotName.createSimple(Embedded.class.getName());

@Override
public void process(ArchiveContext archiveContext, ProcessorContext processorContext) throws Exception {
Expand All @@ -34,21 +39,49 @@ public void process(ArchiveContext archiveContext, ProcessorContext processorCon
// list all entities and create a JPADeploymentTemplate out of it
// Not functional as we will need one deployment template per persistence unit
final IndexView index = archiveContext.getIndex();
Collection<AnnotationInstance> jpaAnnotations = index.getAnnotations(JPA_ENTITY);
if (jpaAnnotations != null && jpaAnnotations.size() > 0) {
// TODO priority?
try (BytecodeRecorder context = processorContext.addStaticInitTask(100)) {
JPADeploymentTemplate template = context.getRecordingProxy(JPADeploymentTemplate.class);
for (AnnotationInstance annotation : jpaAnnotations) {
String entityClass = annotation.target().asClass().toString();
System.out.println(entityClass);
processorContext.addReflectiveClass(true, true, entityClass);
template.addEntity(annotation.target().asClass().toString());
// TODO what priority to give JPA?
try (BytecodeRecorder context = processorContext.addStaticInitTask(100)) {
JPADeploymentTemplate template = context.getRecordingProxy(JPADeploymentTemplate.class);
enlistJPAModelClasses(JPA_ENTITY, processorContext, template, index);
enlistJPAModelClasses(EMBEDDABLE, processorContext, template, index);
enlistReturnType(processorContext, index);

template.enlistPersistenceUnit();
}
}

private void enlistReturnType(ProcessorContext processorContext, IndexView index) {
Collection<AnnotationInstance> annotations = index.getAnnotations(EMBEDDED);
if (annotations != null && annotations.size() > 0) {
for (AnnotationInstance annotation : annotations) {
AnnotationTarget target = annotation.target();
String jpaClassName = null;
switch (target.kind()) {
case FIELD:
// TODO could fail if that's an array or a generic type
jpaClassName = target.asField().type().toString();
break;
case METHOD:
// TODO could fail if that's an array or a generic type
jpaClassName = target.asMethod().returnType().toString();
break;
default:
throw new IllegalStateException("[internal error] @Embedded placed on a unknown element: " + target);
}
template.enlistPersistenceUnit();
processorContext.addReflectiveClass(true, true, jpaClassName);
}
}
}

private void enlistJPAModelClasses(DotName dotName, ProcessorContext processorContext, JPADeploymentTemplate template, IndexView index) {
Collection<AnnotationInstance> jpaAnnotations = index.getAnnotations(dotName);
if (jpaAnnotations != null && jpaAnnotations.size() > 0) {
for (AnnotationInstance annotation : jpaAnnotations) {
String entityClass = annotation.target().asClass().toString();
processorContext.addReflectiveClass(true, true, entityClass);
template.addEntity(annotation.target().asClass().toString());
}
}
}

@Override
Expand Down

0 comments on commit c7e79a9

Please sign in to comment.