package org.jboss.windup.rules.apps.javaee.rules;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.windup.ast.java.data.TypeReferenceLocation;
import org.jboss.windup.config.AbstractRuleProvider;
import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.config.Variables;
import org.jboss.windup.config.loader.RuleLoaderContext;
import org.jboss.windup.config.metadata.RuleMetadata;
import org.jboss.windup.config.operation.Iteration;
import org.jboss.windup.config.operation.iteration.AbstractIterationOperation;
import org.jboss.windup.config.phase.InitialAnalysisPhase;
import org.jboss.windup.graph.model.WindupVertexFrame;
import org.jboss.windup.graph.service.GraphService;
import org.jboss.windup.config.projecttraversal.ProjectTraversalCache;
import org.jboss.windup.rules.apps.java.condition.JavaClass;
import org.jboss.windup.rules.apps.java.model.AbstractJavaSourceModel;
import org.jboss.windup.rules.apps.java.model.JavaClassModel;
import org.jboss.windup.rules.apps.java.scan.ast.JavaTypeReferenceModel;
import org.jboss.windup.rules.apps.java.scan.ast.annotations.JavaAnnotationListTypeValueModel;
import org.jboss.windup.rules.apps.java.scan.ast.annotations.JavaAnnotationLiteralTypeValueModel;
import org.jboss.windup.rules.apps.java.scan.ast.annotations.JavaAnnotationTypeReferenceModel;
import org.jboss.windup.rules.apps.java.scan.ast.annotations.JavaAnnotationTypeValueModel;
import org.jboss.windup.rules.apps.java.scan.provider.AnalyzeJavaFilesRuleProvider;
import org.jboss.windup.rules.apps.javaee.model.JPAEntityModel;
import org.jboss.windup.rules.apps.javaee.model.JPANamedQueryModel;
import org.jboss.windup.rules.apps.javaee.service.JPAEntityService;
import org.jboss.windup.util.Logging;
import org.ocpsoft.rewrite.config.Configuration;
import org.ocpsoft.rewrite.config.ConfigurationBuilder;
import org.ocpsoft.rewrite.context.EvaluationContext;
/**
* Scans for classes with JPA related annotations, and adds JPA related metadata for these.
*
* @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a>
* @author <a href="mailto:bradsdavis@gmail.com">Brad Davis</a>
*/
@RuleMetadata(phase = InitialAnalysisPhase.class, after = AnalyzeJavaFilesRuleProvider.class)
public class DiscoverJPAAnnotationsRuleProvider extends AbstractRuleProvider
{
private static final Logger LOG = Logging.get(DiscoverJPAAnnotationsRuleProvider.class);
private static final String ENTITY_ANNOTATIONS = "entityAnnotations";
private static final String TABLE_ANNOTATIONS_LIST = "tableAnnotations";
private static final String NAMED_QUERY_LIST = "namedQuery";
private static final String NAMED_QUERIES_LIST = "namedQueries";
@Override
public Configuration getConfiguration(RuleLoaderContext ruleLoaderContext)
{
String ruleIDPrefix = getClass().getSimpleName();
return ConfigurationBuilder.begin().addRule()
.when(JavaClass
.references("javax.persistence.Entity").at(TypeReferenceLocation.ANNOTATION).as(ENTITY_ANNOTATIONS)
.or(JavaClass.references("javax.persistence.Table").at(TypeReferenceLocation.ANNOTATION).as(TABLE_ANNOTATIONS_LIST))
.or(JavaClass.references("javax.persistence.NamedQuery").at(TypeReferenceLocation.ANNOTATION).as(NAMED_QUERY_LIST))
.or(JavaClass.references("javax.persistence.NamedQueries").at(TypeReferenceLocation.ANNOTATION).as(NAMED_QUERIES_LIST))
)
.perform(Iteration.over(ENTITY_ANNOTATIONS).perform(new AbstractIterationOperation<JavaTypeReferenceModel>()
{
@Override
public void perform(GraphRewrite event, EvaluationContext context, JavaTypeReferenceModel payload)
{
extractEntityBeanMetadata(event, payload);
}
}).endIteration())
.withId(ruleIDPrefix + "_JPAEntityBeanRule");
}
private String getAnnotationLiteralValue(JavaAnnotationTypeReferenceModel model, String name)
{
JavaAnnotationTypeValueModel valueModel = model.getAnnotationValues().get(name);
if (valueModel instanceof JavaAnnotationLiteralTypeValueModel)
{
JavaAnnotationLiteralTypeValueModel literalTypeValue = (JavaAnnotationLiteralTypeValueModel) valueModel;
return literalTypeValue.getLiteralValue();
}
else
{
return null;
}
}
private void extractEntityBeanMetadata(GraphRewrite event, JavaTypeReferenceModel entityTypeReference)
{
LOG.log(Level.INFO, () -> "extractEntityBeanMetadata() with " + entityTypeReference.getDescription());
entityTypeReference.getFile().setGenerateSourceReport(true);
JavaAnnotationTypeReferenceModel entityAnnotationTypeReference = (JavaAnnotationTypeReferenceModel) entityTypeReference;
JavaAnnotationTypeReferenceModel tableAnnotationTypeReference = null;
final Iterable<? extends WindupVertexFrame> annotations_list = Variables.instance(event).findVariable(TABLE_ANNOTATIONS_LIST);
if (annotations_list != null)
{
for (WindupVertexFrame annotationTypeReferenceBase : annotations_list)
{
JavaAnnotationTypeReferenceModel annotationTypeReference = (JavaAnnotationTypeReferenceModel) annotationTypeReferenceBase;
if (annotationTypeReference.getFile().equals(entityTypeReference.getFile()))
{
tableAnnotationTypeReference = annotationTypeReference;
break;
}
}
}
JavaClassModel ejbClass = getJavaClass(entityTypeReference);
String ejbName = getAnnotationLiteralValue(entityAnnotationTypeReference, "name");
if (ejbName == null)
{
ejbName = ejbClass.getClassName();
}
String tableName = tableAnnotationTypeReference == null ? ejbName : getAnnotationLiteralValue(tableAnnotationTypeReference, "name");
if (tableName == null)
{
tableName = ejbName;
}
String catalogName = tableAnnotationTypeReference == null ? null : getAnnotationLiteralValue(tableAnnotationTypeReference, "catalog");
String schemaName = tableAnnotationTypeReference == null ? null : getAnnotationLiteralValue(tableAnnotationTypeReference, "schema");
JPAEntityService jpaService = new JPAEntityService(event.getGraphContext());
JPAEntityModel jpaEntity = jpaService.create();
jpaEntity.setApplications(ProjectTraversalCache.getApplicationsForProject(event.getGraphContext(), entityTypeReference.getFile().getProjectModel()));
jpaEntity.setEntityName(ejbName);
jpaEntity.setJavaClass(ejbClass);
jpaEntity.setTableName(tableName);
jpaEntity.setCatalogName(catalogName);
jpaEntity.setSchemaName(schemaName);
GraphService<JPANamedQueryModel> namedQueryService = new GraphService<>(event.getGraphContext(), JPANamedQueryModel.class);
Iterable<? extends WindupVertexFrame> namedQueriesList = Variables.instance(event).findVariable(NAMED_QUERIES_LIST);
if (namedQueriesList != null)
{
for (WindupVertexFrame annotationTypeReferenceBase : namedQueriesList)
{
JavaAnnotationTypeReferenceModel annotationTypeReference = (JavaAnnotationTypeReferenceModel) annotationTypeReferenceBase;
if (!annotationTypeReference.getFile().equals(entityTypeReference.getFile()))
continue;
JavaAnnotationTypeValueModel value = annotationTypeReference.getAnnotationValues().get("value");
if (value == null || ! (value instanceof JavaAnnotationListTypeValueModel))
continue;
JavaAnnotationListTypeValueModel referenceList = (JavaAnnotationListTypeValueModel) value;
if (referenceList.getList() != null)
{
for (JavaAnnotationTypeValueModel ref : referenceList.getList())
{
if (ref instanceof JavaAnnotationTypeReferenceModel)
{
JavaAnnotationTypeReferenceModel reference = (JavaAnnotationTypeReferenceModel) ref;
addNamedQuery(namedQueryService, jpaEntity, reference);
}
else
LOG.warning("Unexpected Annotation in " + ref.toPrettyString());
}
}
}
}
Iterable<? extends WindupVertexFrame> namedQueryList = Variables.instance(event).findVariable(NAMED_QUERY_LIST);
if (namedQueryList != null)
{
for (WindupVertexFrame annotationTypeReferenceBase : namedQueryList)
{
JavaAnnotationTypeReferenceModel annotationTypeReference = (JavaAnnotationTypeReferenceModel) annotationTypeReferenceBase;
if (annotationTypeReference.getFile().equals(entityTypeReference.getFile()))
{
JavaAnnotationTypeReferenceModel reference = annotationTypeReference;
addNamedQuery(namedQueryService, jpaEntity, reference);
}
}
}
}
private void addNamedQuery(GraphService<JPANamedQueryModel> namedQueryService, JPAEntityModel jpaEntity,
JavaAnnotationTypeReferenceModel reference)
{
String name = getAnnotationLiteralValue(reference, "name");
String query = getAnnotationLiteralValue(reference, "query");
LOG.info("Found query: " + name + " -> " + query);
JPANamedQueryModel namedQuery = namedQueryService.create();
namedQuery.setQueryName(name);
namedQuery.setQuery(query);
namedQuery.setJpaEntity(jpaEntity);
}
private JavaClassModel getJavaClass(JavaTypeReferenceModel javaTypeReference)
{
JavaClassModel result = null;
AbstractJavaSourceModel javaSource = javaTypeReference.getFile();
for (JavaClassModel javaClassModel : javaSource.getJavaClasses())
{
// there can be only one public one, and the annotated class should be public
if (javaClassModel.isPublic() != null && javaClassModel.isPublic())
{
result = javaClassModel;
break;
}
}
if (result == null)
{
// no public classes found, so try to find any class (even non-public ones)
result = javaSource.getJavaClasses().iterator().next();
}
return result;
}
@Override
public String toString()
{
return "DiscoverEJBAnnotatedClasses";
}
}