From 46cdc84e6b905cf9a6a3bc90cf76145eb102f7a3 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Fri, 1 Sep 2017 10:25:10 +0200 Subject: [PATCH] Refactor/findannotation (#1104) * refactor: renamed findAnnotation method that searches recursive to findAnnotationRecursive * CHG: searching namedQueries & indices also recursive in superclasses * FIX: Using the AnnotationBase methods on several places and documented them --- .../config/AbstractNamingConvention.java | 29 ++-------- .../server/cache/DefaultCacheHolder.java | 3 +- .../changelog/DefaultChangeLogRegister.java | 2 +- .../server/core/bootup/BootupClasses.java | 6 +- .../server/deploy/BeanDescriptorManager.java | 5 +- .../server/deploy/parse/AnnotationBase.java | 55 ++++++++++++++++--- .../server/deploy/parse/AnnotationClass.java | 32 +++++------ .../server/deploy/parse/AnnotationSql.java | 2 +- .../server/deploy/parse/DeployInherit.java | 8 +-- .../server/type/DefaultTypeManager.java | 5 +- 10 files changed, 87 insertions(+), 60 deletions(-) diff --git a/src/main/java/io/ebean/config/AbstractNamingConvention.java b/src/main/java/io/ebean/config/AbstractNamingConvention.java index b435d2e46..e123bec55 100644 --- a/src/main/java/io/ebean/config/AbstractNamingConvention.java +++ b/src/main/java/io/ebean/config/AbstractNamingConvention.java @@ -1,13 +1,13 @@ package io.ebean.config; import io.ebean.config.dbplatform.DatabasePlatform; +import io.ebeaninternal.server.deploy.parse.AnnotationBase; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.persistence.DiscriminatorValue; import javax.persistence.Inheritance; import javax.persistence.Table; -import java.lang.annotation.Annotation; /** * Provides some base implementation for NamingConventions. @@ -219,15 +219,10 @@ public abstract class AbstractNamingConvention implements NamingConvention { * Return true if this class is part of entity inheritance. */ protected boolean hasInheritance(Class supCls) { - return hasAnnotation(supCls, Inheritance.class) || hasAnnotation(supCls, DiscriminatorValue.class); + return AnnotationBase.findAnnotationRecursive(supCls, Inheritance.class) != null + || AnnotationBase.findAnnotation(supCls, DiscriminatorValue.class) != null; } - /** - * Return true if the class has the given annotation. - */ - protected boolean hasAnnotation(Class supCls, Class annotation) { - return supCls.getAnnotation(annotation) != null; - } @Override public TableName getM2MJoinTableName(TableName lhsTable, TableName rhsTable) { @@ -258,7 +253,7 @@ public abstract class AbstractNamingConvention implements NamingConvention { */ protected TableName getTableNameFromAnnotation(Class beanClass) { - final Table t = findTableAnnotation(beanClass); + final Table t = AnnotationBase.findAnnotationRecursive(beanClass, Table.class); // Take the annotation if defined if (t != null && !isEmpty(t.name())) { @@ -271,22 +266,6 @@ public abstract class AbstractNamingConvention implements NamingConvention { return null; } - /** - * Search recursively for an @Table in the class hierarchy. - */ - protected Table findTableAnnotation(Class cls) { - while (true) { - if (cls.equals(Object.class)) { - return null; - } - Table table = cls.getAnnotation(Table.class); - if (table != null) { - return table; - } - cls = cls.getSuperclass(); - } - } - /** * Replace back ticks (if they are used) with database platform specific * quoted identifiers. diff --git a/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java b/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java index 996f82684..eb87d393b 100644 --- a/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java +++ b/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java @@ -7,6 +7,7 @@ import io.ebean.cache.ServerCacheFactory; import io.ebean.cache.ServerCacheOptions; import io.ebean.cache.ServerCacheType; import io.ebean.config.CurrentTenantProvider; +import io.ebeaninternal.server.deploy.parse.AnnotationBase; import java.util.concurrent.ConcurrentHashMap; @@ -82,7 +83,7 @@ class DefaultCacheHolder { } private ServerCacheOptions getQueryOptions(Class cls) { - CacheQueryTuning tuning = cls.getAnnotation(CacheQueryTuning.class); + CacheQueryTuning tuning = AnnotationBase.findAnnotation(cls, CacheQueryTuning.class); if (tuning != null) { return new ServerCacheOptions(tuning).applyDefaults(queryDefault); } diff --git a/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogRegister.java b/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogRegister.java index 80e6620f8..bd9b4210c 100644 --- a/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogRegister.java +++ b/src/main/java/io/ebeaninternal/server/changelog/DefaultChangeLogRegister.java @@ -52,7 +52,7 @@ public class DefaultChangeLogRegister implements ChangeLogRegister { * Find and return the ChangeLog annotation in the inheritance hierarchy. */ private ChangeLog getChangeLog(Class beanType) { - return AnnotationBase.findAnnotation(beanType, ChangeLog.class); + return AnnotationBase.findAnnotationRecursive(beanType, ChangeLog.class); } /** diff --git a/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java b/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java index ad82e833b..5e57ed8e7 100644 --- a/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java +++ b/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java @@ -16,6 +16,7 @@ import io.ebean.event.changelog.ChangeLogPrepare; import io.ebean.event.changelog.ChangeLogRegister; import io.ebean.event.readaudit.ReadAuditLogger; import io.ebean.event.readaudit.ReadAuditPrepare; +import io.ebeaninternal.server.deploy.parse.AnnotationBase; import io.ebeaninternal.server.type.ScalarType; import org.avaje.classpath.scanner.ClassFilter; import org.slf4j.Logger; @@ -470,7 +471,10 @@ public class BootupClasses implements ClassFilter { return has(cls, Embeddable.class); } + /** + * Returns true if this class has the annotation (or meta annotation). Does not search recursively. + */ private boolean has(Class cls, Class ann) { - return cls.getAnnotation(ann) != null; + return AnnotationBase.findAnnotation(cls, ann) != null; } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java index 9554b8470..32faf53fa 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -38,6 +38,7 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; import io.ebeaninternal.server.deploy.meta.DeployBeanTable; import io.ebeaninternal.server.deploy.meta.DeployTableJoin; +import io.ebeaninternal.server.deploy.parse.AnnotationBase; import io.ebeaninternal.server.deploy.parse.DeployBeanInfo; import io.ebeaninternal.server.deploy.parse.DeployCreateProperties; import io.ebeaninternal.server.deploy.parse.DeployInherit; @@ -1451,8 +1452,8 @@ 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); + // do not search recursive here + MappedSuperclass annotation = AnnotationBase.findAnnotation(beanClass, MappedSuperclass.class); if (annotation == null) { return false; } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationBase.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationBase.java index 9dcbd74c1..0aeb20e69 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationBase.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationBase.java @@ -20,7 +20,29 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** - * Provides some base methods for processing deployment annotations. + * Provides some base methods for processing deployment annotations. All findAnnotation* methods + * are capable to search for meta-annotations (annotation that has an other annotation) + * + *

search algorithm for ONE annotation:

+ * + * DFS (Depth-First-Search) is used. The algorithm is the same as it is used in Spring-Framework, + * as the code is taken from there. + * + *

search algoritm for a Set<Annotation> works a litte bit different, as it does not stop + * on the first match, but continues searching down to the last corner to find all annotations.

+ * + *

To prevent endless recursion, the search algoritm tracks all visited annotations

+ * + *

Supports also "java 1.6 repeatable containers" like{@link JoinColumn} / {@link JoinColumns}.

+ * + *

This means, searching for JoinColumn will find them also if they are inside a + * JoinColumns annotation

*/ public abstract class AnnotationBase { @@ -51,12 +73,13 @@ public abstract class AnnotationBase { /** * Return the annotation for the property. *

- * Looks first at the field and then at the getter method. + * Looks first at the field and then at the getter method. It searches for meta-annotations, but not + * recursively in the class hierarchy. *

*

* If a repeatable annotation class is specified and the annotation is platform * specific(see {@link #getPlatformMatchingAnnotation(Set, Platform)}), then the platform specific - * annotation is returned. Otherwise the first annotation is returned. Note that you must no longer + * annotation is returned. Otherwise the first annotation is returned. Note that you need no longer * handle "java 1.6 repeatable containers" like {@link JoinColumn} / {@link JoinColumns} yourself. *

*

@@ -101,6 +124,7 @@ public abstract class AnnotationBase { * Return the annotation for the property. *

* Looks first at the field and then at the getter method. then at class level. + * (This is used for SequenceGenerator e.g.) *

*/ protected T find(DeployBeanProperty prop, Class annClass) { @@ -134,7 +158,7 @@ public abstract class AnnotationBase { if (annotationType == null) { return null; } - // check if directly present, if not, start recursive traversal + // check if directly present, if not, start search for meta-annotations. A ann = annotatedElement.getAnnotation(annotationType); if (ann != null) { return ann; @@ -149,11 +173,11 @@ public abstract class AnnotationBase { * the supplied element. *

Note: this method searches for annotations at class & superClass(es)! */ - public static A findAnnotation(Class clazz, Class annotationType) { + public static A findAnnotationRecursive(Class clazz, Class annotationType) { if (annotationType == null) { return null; } - // check if directly present, if not, start recursive traversal + // check if directly present, if not, start search for meta-annotations. A ann = clazz.getAnnotation(annotationType); if (ann != null) { return ann; @@ -163,7 +187,7 @@ public abstract class AnnotationBase { if (ann != null) { return ann; } - // not present at this class - traverse to superclass + // no meta-annotation present at this class - traverse to superclass clazz = clazz.getSuperclass(); } return null; @@ -182,6 +206,22 @@ public abstract class AnnotationBase { return getPlatformMatchingAnnotation(anns, platform); } + /** + * Finds all annotations recusively for a class and its superclasses. + */ + public static Set findAnnotationsRecursive(Class clazz, Class annotationType) { + if (annotationType == null) { + return null; + } + Set ret = new LinkedHashSet<>(); + Set visited = new HashSet<>(); + while (clazz != null && clazz != Object.class) { + findMetaAnnotations(clazz, annotationType, ret, visited); + clazz = clazz.getSuperclass(); + } + return ret; + } + /** * Perform the search algorithm avoiding endless recursion by tracking which * annotations have already been visited. @@ -265,6 +305,7 @@ public abstract class AnnotationBase { } private static final ConcurrentMap valueMethods = new ConcurrentHashMap<>(); + // only a non-null-marker the valueMethods - Cache private static final Method nullMethod = getNullMethod(); diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java index f63fd81a4..fbab35764 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java @@ -67,7 +67,7 @@ public class AnnotationClass extends AnnotationParser { public void parseAttributeOverride() { Class cls = descriptor.getBeanType(); - AttributeOverride override = AnnotationBase.findAnnotation(cls, AttributeOverride.class); + AttributeOverride override = AnnotationBase.findAnnotationRecursive(cls, AttributeOverride.class); if (override != null) { String propertyName = override.name(); Column column = override.column(); @@ -109,14 +109,14 @@ public class AnnotationClass extends AnnotationParser { private void read(Class cls) { // maybe doc store only so check for this before @Entity - DocStore docStore = AnnotationBase.findAnnotation(cls, DocStore.class); + DocStore docStore = AnnotationBase.findAnnotationRecursive(cls, DocStore.class); if (docStore != null) { descriptor.readDocStore(docStore); descriptor.setEntityType(EntityType.DOC); descriptor.setName(cls.getSimpleName()); } - Entity entity = AnnotationBase.findAnnotation(cls, Entity.class); + Entity entity = AnnotationBase.findAnnotationRecursive(cls, Entity.class); if (entity != null) { descriptor.setEntityType(EntityType.ORM); if (entity.name().isEmpty()) { @@ -126,27 +126,27 @@ public class AnnotationClass extends AnnotationParser { } } - Embeddable embeddable = AnnotationBase.findAnnotation(cls, Embeddable.class); + Embeddable embeddable = AnnotationBase.findAnnotationRecursive(cls, Embeddable.class); if (embeddable != null) { descriptor.setEntityType(EntityType.EMBEDDED); descriptor.setName("Embeddable:" + cls.getSimpleName()); } - Set indices = AnnotationBase.findAnnotations(cls, Index.class); + Set indices = AnnotationBase.findAnnotationsRecursive(cls, Index.class); for (Index index : indices) { descriptor.addIndex(new IndexDefinition(index.columnNames(), index.name(), index.unique())); } - UniqueConstraint uc = AnnotationBase.findAnnotation(cls, UniqueConstraint.class); + UniqueConstraint uc = AnnotationBase.findAnnotationRecursive(cls, UniqueConstraint.class); if (uc != null) { descriptor.addIndex(new IndexDefinition(uc.columnNames())); } - View view = AnnotationBase.findAnnotation(cls, View.class); + View view = AnnotationBase.findAnnotationRecursive(cls, View.class); if (view != null) { descriptor.setView(view.name(), view.dependentTables()); } - Table table = AnnotationBase.findAnnotation(cls, Table.class); + Table table = AnnotationBase.findAnnotationRecursive(cls, Table.class); if (table != null) { UniqueConstraint[] uniqueConstraints = table.uniqueConstraints(); for (UniqueConstraint c : uniqueConstraints) { @@ -154,42 +154,42 @@ public class AnnotationClass extends AnnotationParser { } } - Draftable draftable = AnnotationBase.findAnnotation(cls, Draftable.class); + Draftable draftable = AnnotationBase.findAnnotationRecursive(cls, Draftable.class); if (draftable != null) { descriptor.setDraftable(); } - DraftableElement draftableElement = AnnotationBase.findAnnotation(cls, DraftableElement.class); + DraftableElement draftableElement = AnnotationBase.findAnnotationRecursive(cls, DraftableElement.class); if (draftableElement != null) { descriptor.setDraftableElement(); } - ReadAudit readAudit = AnnotationBase.findAnnotation(cls, ReadAudit.class); + ReadAudit readAudit = AnnotationBase.findAnnotationRecursive(cls, ReadAudit.class); if (readAudit != null) { descriptor.setReadAuditing(); } - History history = AnnotationBase.findAnnotation(cls, History.class); + History history = AnnotationBase.findAnnotationRecursive(cls, History.class); if (history != null) { descriptor.setHistorySupport(); } - DbComment comment = AnnotationBase.findAnnotation(cls, DbComment.class); + DbComment comment = AnnotationBase.findAnnotationRecursive(cls, DbComment.class); if (comment != null) { descriptor.setDbComment(comment.value()); } - UpdateMode updateMode = AnnotationBase.findAnnotation(cls, UpdateMode.class); + UpdateMode updateMode = AnnotationBase.findAnnotationRecursive(cls, UpdateMode.class); if (updateMode != null) { descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly()); } - Cache cache = AnnotationBase.findAnnotation(cls, Cache.class); + Cache cache = AnnotationBase.findAnnotationRecursive(cls, Cache.class); if (cache != null && !disableL2Cache) { descriptor.setCache(cache); } - Set namedQueries = AnnotationBase.findAnnotations(cls, NamedQuery.class); + Set namedQueries = AnnotationBase.findAnnotationsRecursive(cls, NamedQuery.class); for (NamedQuery namedQuery : namedQueries) { descriptor.addNamedQuery(namedQuery.name(), namedQuery.query()); } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationSql.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationSql.java index ab3dbb758..aa0205632 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationSql.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationSql.java @@ -15,7 +15,7 @@ public class AnnotationSql extends AnnotationParser { @Override public void parse() { Class cls = descriptor.getBeanType(); - Sql sql = AnnotationBase.findAnnotation(cls, Sql.class); + Sql sql = AnnotationBase.findAnnotationRecursive(cls, Sql.class); if (sql != null) { descriptor.setEntityType(BeanDescriptor.EntityType.SQL); } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/DeployInherit.java b/src/main/java/io/ebeaninternal/server/deploy/parse/DeployInherit.java index 21b4bc82e..30fab9aca 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/DeployInherit.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/DeployInherit.java @@ -109,11 +109,11 @@ public class DeployInherit { info.setParent(parent); } - Inheritance ia = AnnotationBase.findAnnotation(cls, Inheritance.class); + Inheritance ia = AnnotationBase.findAnnotationRecursive(cls, Inheritance.class); if (ia != null) { ia.strategy(); } - DiscriminatorColumn da = AnnotationBase.findAnnotation(cls, DiscriminatorColumn.class); + DiscriminatorColumn da = AnnotationBase.findAnnotationRecursive(cls, DiscriminatorColumn.class); if (da != null) { // lowercase the discriminator column for RawSql and JSON info.setColumnName(da.name().toLowerCase()); @@ -122,7 +122,7 @@ public class DeployInherit { info.setColumnDefn(da.columnDefinition()); } - DiscriminatorValue dv = AnnotationBase.findAnnotation(cls, DiscriminatorValue.class); + DiscriminatorValue dv = AnnotationBase.findAnnotationRecursive(cls, DiscriminatorValue.class); if (dv != null) { info.setDiscriminatorValue(dv.value()); } @@ -144,7 +144,7 @@ public class DeployInherit { if (cls.equals(Object.class)) { return false; } - Annotation a = AnnotationBase.findAnnotation(cls, Inheritance.class); + Annotation a = AnnotationBase.findAnnotationRecursive(cls, Inheritance.class); if (a != null) { return true; } diff --git a/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java index 68c77e9af..121e0c771 100644 --- a/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java +++ b/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java @@ -13,6 +13,7 @@ import io.ebean.config.dbplatform.DbPlatformType; import io.ebean.dbmigration.DbOffline; import io.ebean.plugin.ExtraTypeFactory; import io.ebeaninternal.server.core.bootup.BootupClasses; +import io.ebeaninternal.server.deploy.parse.AnnotationBase; import io.ebeanservice.docstore.api.mapping.DocPropertyType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -543,7 +544,7 @@ public final class DefaultTypeManager implements TypeManager { Field[] fields = enumType.getDeclaredFields(); for (Field field : fields) { - EnumValue enumValue = field.getAnnotation(EnumValue.class); + EnumValue enumValue = AnnotationBase.findAnnotation(field, EnumValue.class); if (enumValue != null) { nameValueMap.put(field.getName(), enumValue.value()); if (integerType && !isIntegerType(enumValue.value())) { @@ -574,7 +575,7 @@ public final class DefaultTypeManager implements TypeManager { Method[] methods = enumType.getMethods(); for (Method method : methods) { - DbEnumValue dbValue = method.getAnnotation(DbEnumValue.class); + DbEnumValue dbValue = AnnotationBase.findAnnotation(method, DbEnumValue.class); if (dbValue != null) { boolean integerValues = DbEnumType.INTEGER == dbValue.storage(); return createEnumScalarTypeDbValue(enumType, method, integerValues);