diff --git a/src/main/java/com/avaje/ebeaninternal/server/changelog/DefaultChangeLogRegister.java b/src/main/java/com/avaje/ebeaninternal/server/changelog/DefaultChangeLogRegister.java index 87c65f8fc..f26cf8543 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/changelog/DefaultChangeLogRegister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/changelog/DefaultChangeLogRegister.java @@ -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); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java index 4f73dffe0..864c34586 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -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; diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index fb5191afa..126f864d0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -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() { diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBase.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBase.java index 57b802168..c3f64f1fc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBase.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBase.java @@ -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 find(DeployBeanProperty prop, Class 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}. + *

Meta-annotations will be searched if the annotation is not + * directly present on the supplied element. + *

Warning: 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!

+ */ + public static A findAnnotation(AnnotatedElement annotatedElement, Class 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()); + } + } + /** + * Find a single {@link Annotation} of {@code annotationType} on the + * supplied {@link clazz}. + *

Meta-annotations will be searched if the annotation is not + * directly present on the supplied element. + *

Note: this method searches for annotations at + * class & superClass(es)!

+ */ + public static
A findAnnotation(Class clazz, Class 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()); + 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 visited. + */ + @SuppressWarnings("unchecked") + private static A findAnnotation(AnnotatedElement annotatedElement, Class annotationType, Set 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; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java index 509133d3d..5b9b95da1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java @@ -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()); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java index 34c3e8941..d84fca20c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java @@ -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); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployCreateProperties.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployCreateProperties.java index 23ab68dec..f1a4224e8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployCreateProperties.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployCreateProperties.java @@ -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); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInherit.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInherit.java index ada54e038..b110798f9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInherit.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInherit.java @@ -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; }