ENH: meta-annotation support similar to spring.

See http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-meta-annotations for detailed explanation
This commit is contained in:
Roland Praml
2016-07-30 12:44:14 +02:00
parent 780ee7c7c0
commit 3183287833
8 changed files with 135 additions and 51 deletions
@@ -5,6 +5,7 @@ import com.avaje.ebean.annotation.ChangeLogInsertMode;
import com.avaje.ebean.event.BeanPersistRequest;
import com.avaje.ebean.event.changelog.ChangeLogFilter;
import com.avaje.ebean.event.changelog.ChangeLogRegister;
import com.avaje.ebeaninternal.server.deploy.parse.AnnotationBase;
import java.util.HashSet;
import java.util.Set;
@@ -52,14 +53,7 @@ public class DefaultChangeLogRegister implements ChangeLogRegister {
* Find and return the ChangeLog annotation in the inheritance hierarchy.
*/
private ChangeLog getChangeLog(Class<?> beanType) {
ChangeLog changeLog = beanType.getAnnotation(ChangeLog.class);
if (changeLog != null) {
return changeLog;
}
if (Object.class.equals(beanType.getSuperclass())) {
return null;
}
return getChangeLog(beanType.getSuperclass());
return AnnotationBase.findAnnotation(beanType, ChangeLog.class);
}
/**
@@ -38,6 +38,7 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
import com.avaje.ebeaninternal.server.deploy.parse.AnnotationBase;
import com.avaje.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
@@ -1457,7 +1458,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
* If so it is ok for it not to be enhanced.
*/
private boolean isMappedSuperWithNoProperties(Class<?> beanClass) {
// Attention: do not use AnnotationBase.findAnnotation(cls,...) here.
MappedSuperclass annotation = beanClass.getAnnotation(MappedSuperclass.class);
if (annotation == null) {
return false;
@@ -18,6 +18,7 @@ import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.DeployDocPropertyOptions;
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import com.avaje.ebeaninternal.server.deploy.parse.AnnotationBase;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.properties.BeanPropertyGetter;
import com.avaje.ebeaninternal.server.properties.BeanPropertySetter;
@@ -233,29 +234,29 @@ public class DeployBeanProperty {
if (field == null) {
return 0;
}
if (field.getAnnotation(Id.class) != null) {
if (AnnotationBase.findAnnotation(field, Id.class) != null) {
return ID_ORDER;
} else if (field.getAnnotation(EmbeddedId.class) != null) {
} else if (AnnotationBase.findAnnotation(field, EmbeddedId.class) != null) {
return ID_ORDER;
} else if (undirectionalShadow) {
return UNIDIRECTIONAL_ORDER;
} else if (isAuditProperty()) {
return AUDITCOLUMN_ORDER;
} else if (field.getAnnotation(Version.class) != null) {
} else if (AnnotationBase.findAnnotation(field, Version.class) != null) {
return VERSIONCOLUMN_ORDER;
} else if (field.getAnnotation(SoftDelete.class) != null) {
} else if (AnnotationBase.findAnnotation(field, SoftDelete.class) != null) {
return VERSIONCOLUMN_ORDER;
}
return 0;
}
private boolean isAuditProperty() {
return (field.getAnnotation(WhenCreated.class) != null
|| field.getAnnotation(WhenModified.class) != null
|| field.getAnnotation(WhoModified.class) != null
|| field.getAnnotation(WhoCreated.class) != null
|| field.getAnnotation(UpdatedTimestamp.class) != null
|| field.getAnnotation(CreatedTimestamp.class) != null);
return (AnnotationBase.findAnnotation(field, WhenCreated.class) != null
|| AnnotationBase.findAnnotation(field, WhenModified.class) != null
|| AnnotationBase.findAnnotation(field, WhoModified.class) != null
|| AnnotationBase.findAnnotation(field, WhoCreated.class) != null
|| AnnotationBase.findAnnotation(field, UpdatedTimestamp.class) != null
|| AnnotationBase.findAnnotation(field, CreatedTimestamp.class) != null);
}
public String getFullBeanName() {
@@ -1,8 +1,11 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import com.avaje.ebean.config.NamingConvention;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
@@ -46,12 +49,12 @@ public abstract class AnnotationBase {
T a = null;
Field field = prop.getField();
if (field != null) {
a = field.getAnnotation(annClass);
a = findAnnotation(field, annClass);
}
if (a == null) {
Method m = prop.getReadMethod();
if (m != null) {
a = m.getAnnotation(annClass);
a = findAnnotation(m, annClass);
}
}
return a;
@@ -66,10 +69,95 @@ public abstract class AnnotationBase {
protected <T extends Annotation> T find(DeployBeanProperty prop, Class<T> annClass) {
T a = get(prop, annClass);
if (a == null) {
a = prop.getOwningType().getAnnotation(annClass);
a = findAnnotation(prop.getOwningType(),annClass);
}
return a;
}
// this code is taken from the spring framework to find annotations recursively
/**
* Determine if the supplied {@link Annotation} is defined in the core JDK
* {@code java.lang.annotation} package.
*/
public static boolean isInJavaLangAnnotationPackage(Annotation annotation) {
return annotation.annotationType().getName().startsWith("java.lang.annotation");
}
/**
* Find a single {@link Annotation} of {@code annotationType} on the
* supplied {@link AnnotatedElement}.
* <p>Meta-annotations will be searched if the annotation is not
* <em>directly present</em> on the supplied element.
* <p><strong>Warning</strong>: this method operates generically on
* annotated elements. In other words, this method does not execute
* specialized search algorithms for classes or methods. It only
* traverses through Annotations!</p>
*/
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
// check if directly present, if not, start recursive traversal
A ann = annotatedElement.getAnnotation(annotationType);
if (ann != null) {
return ann;
} else {
return findAnnotation(annotatedElement, annotationType, new HashSet<Annotation>());
}
}
/**
* Find a single {@link Annotation} of {@code annotationType} on the
* supplied {@link clazz}.
* <p>Meta-annotations will be searched if the annotation is not
* <em>directly present</em> on the supplied element.
* <p><strong>Note</strong>: this method searches for annotations at
* class & superClass(es)!</p>
*/
public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
// check if directly present, if not, start recursive traversal
A ann = clazz.getAnnotation(annotationType);
if (ann != null) {
return ann;
} else {
while (clazz != null && clazz != Object.class) {
ann = findAnnotation(clazz, annotationType, new HashSet<Annotation>());
if (ann != null) {
return ann;
}
// not present at this class - traverse to superclass
clazz = clazz.getSuperclass();
}
return null;
}
}
/**
* Perform the search algorithm for {@link #findAnnotation(AnnotatedElement, Class)}
* avoiding endless recursion by tracking which annotations have already
* been <em>visited</em>.
*/
@SuppressWarnings("unchecked")
private static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType, Set<Annotation> visited) {
Annotation[] anns = annotatedElement.getDeclaredAnnotations();
for (Annotation ann : anns) {
if (ann.annotationType() == annotationType) {
return (A) ann;
}
}
for (Annotation ann : anns) {
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
A annotation = findAnnotation((AnnotatedElement) ann.annotationType(), annotationType, visited);
if (annotation != null) {
return annotation;
}
}
}
return null;
}
}
@@ -66,7 +66,7 @@ public class AnnotationClass extends AnnotationParser {
public void parseAttributeOverride() {
Class<?> cls = descriptor.getBeanType();
AttributeOverride override = cls.getAnnotation(AttributeOverride.class);
AttributeOverride override = AnnotationBase.findAnnotation(cls,AttributeOverride.class);
if (override != null) {
String propertyName = override.name();
Column column = override.column();
@@ -103,7 +103,7 @@ public class AnnotationClass extends AnnotationParser {
private void read(Class<?> cls) {
Entity entity = cls.getAnnotation(Entity.class);
Entity entity = AnnotationBase.findAnnotation(cls,Entity.class);
if (entity != null) {
if (entity.name().equals("")) {
descriptor.setName(cls.getSimpleName());
@@ -112,27 +112,27 @@ public class AnnotationClass extends AnnotationParser {
}
}
Embeddable embeddable = cls.getAnnotation(Embeddable.class);
Embeddable embeddable = AnnotationBase.findAnnotation(cls,Embeddable.class);
if (embeddable != null) {
descriptor.setEntityType(EntityType.EMBEDDED);
descriptor.setName("Embeddable:" + cls.getSimpleName());
}
Index index = cls.getAnnotation(Index.class);
Index index = AnnotationBase.findAnnotation(cls,Index.class);
if (index != null) {
descriptor.addIndex(new IndexDefinition(index.columnNames(), index.name(), index.unique()));
}
UniqueConstraint uc = cls.getAnnotation(UniqueConstraint.class);
UniqueConstraint uc = AnnotationBase.findAnnotation(cls,UniqueConstraint.class);
if (uc != null) {
descriptor.addIndex(new IndexDefinition(uc.columnNames()));
}
View view = cls.getAnnotation(View.class);
View view = AnnotationBase.findAnnotation(cls,View.class);
if (view != null) {
descriptor.setView(view.name(), view.dependentTables());
}
Table table = cls.getAnnotation(Table.class);
Table table = AnnotationBase.findAnnotation(cls,Table.class);
if (table != null) {
UniqueConstraint[] uniqueConstraints = table.uniqueConstraints();
for (UniqueConstraint c : uniqueConstraints) {
@@ -140,54 +140,54 @@ public class AnnotationClass extends AnnotationParser {
}
}
Draftable draftable = cls.getAnnotation(Draftable.class);
Draftable draftable = AnnotationBase.findAnnotation(cls,Draftable.class);
if (draftable != null) {
descriptor.setDraftable();
}
DraftableElement draftableElement = cls.getAnnotation(DraftableElement.class);
DraftableElement draftableElement = AnnotationBase.findAnnotation(cls,DraftableElement.class);
if (draftableElement != null) {
descriptor.setDraftableElement();
}
ReadAudit readAudit = cls.getAnnotation(ReadAudit.class);
ReadAudit readAudit = AnnotationBase.findAnnotation(cls,ReadAudit.class);
if (readAudit != null) {
descriptor.setReadAuditing();
}
History history = cls.getAnnotation(History.class);
History history = AnnotationBase.findAnnotation(cls,History.class);
if (history != null) {
descriptor.setHistorySupport();
}
DbComment comment = cls.getAnnotation(DbComment.class);
DbComment comment = AnnotationBase.findAnnotation(cls,DbComment.class);
if (comment != null) {
descriptor.setDbComment(comment.value());
}
DocStore docStore = cls.getAnnotation(DocStore.class);
DocStore docStore = AnnotationBase.findAnnotation(cls,DocStore.class);
if (docStore != null) {
descriptor.readDocStore(docStore);
}
UpdateMode updateMode = cls.getAnnotation(UpdateMode.class);
UpdateMode updateMode = AnnotationBase.findAnnotation(cls,UpdateMode.class);
if (updateMode != null) {
descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly());
}
Cache cache = cls.getAnnotation(Cache.class);
Cache cache = AnnotationBase.findAnnotation(cls,Cache.class);
if (cache != null && !disableL2Cache) {
descriptor.setCache(cache);
}
NamedQueries namedQueries = cls.getAnnotation(NamedQueries.class);
NamedQueries namedQueries = AnnotationBase.findAnnotation(cls,NamedQueries.class);
if (namedQueries != null) {
for (NamedQuery namedQuery : namedQueries.value()) {
descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
}
}
NamedQuery namedQuery = cls.getAnnotation(NamedQuery.class);
NamedQuery namedQuery = AnnotationBase.findAnnotation(cls,NamedQuery.class);
if (namedQuery != null) {
descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
}
@@ -14,7 +14,7 @@ public class AnnotationSql extends AnnotationParser {
public void parse() {
Class<?> cls = descriptor.getBeanType();
Sql sql = cls.getAnnotation(Sql.class);
Sql sql = AnnotationBase.findAnnotation(cls,Sql.class);
if (sql != null) {
descriptor.setEntityType(BeanDescriptor.EntityType.SQL);
}
@@ -223,7 +223,7 @@ public class DeployCreateProperties {
Class<?> propertyType = field.getType();
ManyToOne manyToOne = field.getAnnotation(ManyToOne.class);
ManyToOne manyToOne = AnnotationBase.findAnnotation(field,ManyToOne.class);
if (manyToOne != null){
Class<?> tt = manyToOne.targetEntity();
if (!tt.equals(void.class)){
@@ -240,7 +240,7 @@ public class DeployCreateProperties {
// List, Set or Map based object
Class<?> targetType = determineTargetType(field);
if (targetType == null) {
Transient transAnnotation = field.getAnnotation(Transient.class);
Transient transAnnotation = AnnotationBase.findAnnotation(field,Transient.class);
if (transAnnotation != null) {
// not supporting this field (generic type used)
return null;
@@ -298,15 +298,15 @@ public class DeployCreateProperties {
* Return true if the field has one of the special mappings.
*/
private boolean isSpecialScalarType(Field field) {
return (field.getAnnotation(DbJson.class) != null)
|| (field.getAnnotation(DbJsonB.class) != null)
|| (field.getAnnotation(DbArray.class) != null)
|| (field.getAnnotation(DbHstore.class) != null);
return (AnnotationBase.findAnnotation(field,DbJson.class) != null)
|| (AnnotationBase.findAnnotation(field,DbJsonB.class) != null)
|| (AnnotationBase.findAnnotation(field,DbArray.class) != null)
|| (AnnotationBase.findAnnotation(field,DbHstore.class) != null);
}
private boolean isTransientField(Field field) {
Transient t = field.getAnnotation(Transient.class);
Transient t = AnnotationBase.findAnnotation(field,Transient.class);
return (t != null);
}
@@ -111,11 +111,11 @@ public class DeployInherit {
info.setParent(parent);
}
Inheritance ia = cls.getAnnotation(Inheritance.class);
Inheritance ia = AnnotationBase.findAnnotation(cls,Inheritance.class);
if (ia != null) {
ia.strategy();
}
DiscriminatorColumn da = cls.getAnnotation(DiscriminatorColumn.class);
DiscriminatorColumn da = AnnotationBase.findAnnotation(cls,DiscriminatorColumn.class);
if (da != null) {
// lowercase the discriminator column for RawSql and JSON
info.setDiscriminatorColumn(da.name().toLowerCase());
@@ -128,7 +128,7 @@ public class DeployInherit {
info.setDiscriminatorLength(da.length());
}
DiscriminatorValue dv = cls.getAnnotation(DiscriminatorValue.class);
DiscriminatorValue dv = AnnotationBase.findAnnotation(cls,DiscriminatorValue.class);
if (dv != null) {
info.setDiscriminatorValue(dv.value());
}
@@ -149,7 +149,7 @@ public class DeployInherit {
if (cls.equals(Object.class)) {
return false;
}
Annotation a = cls.getAnnotation(Inheritance.class);
Annotation a = AnnotationBase.findAnnotation(cls,Inheritance.class);
if (a != null) {
return true;
}