From d09ede6c23e2cd121b79e95b1ef4374a8ca6a366 Mon Sep 17 00:00:00 2001 From: Andi Huber Date: Tue, 12 Nov 2024 05:38:51 +0100 Subject: [PATCH] CAUSEWAY-2297: move attribute enum to class cache --- .../internal/reflection/_ClassCache.java | 75 ++++++++++--------- .../beans/CausewayBeanTypeClassifier.java | 72 ++++++------------ .../beans/CausewayComponentCollector.java | 4 +- .../RemoveAnnotatedMethodsFacetFactory.java | 5 +- .../dflt/ObjectSpecificationDefault.java | 6 +- 5 files changed, 69 insertions(+), 93 deletions(-) diff --git a/commons/src/main/java/org/apache/causeway/commons/internal/reflection/_ClassCache.java b/commons/src/main/java/org/apache/causeway/commons/internal/reflection/_ClassCache.java index 931bdd25dbf..acf7a702014 100644 --- a/commons/src/main/java/org/apache/causeway/commons/internal/reflection/_ClassCache.java +++ b/commons/src/main/java/org/apache/causeway/commons/internal/reflection/_ClassCache.java @@ -18,6 +18,7 @@ */ package org.apache.causeway.commons.internal.reflection; +import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -37,6 +38,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.lang.Nullable; @@ -80,6 +82,17 @@ */ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class _ClassCache implements AutoCloseable { + + public enum Attribute { + /** + * Corresponds to the bean name of Spring managed beans. + */ + SPRING_NAMED, + /** + * Corresponds to DomainObject#mixinMethod(). + */ + MIXIN_MAIN_METHOD_NAME; + } public static _ClassCache getInstance() { return _Context.computeIfAbsent(_ClassCache.class, ()->new _ClassCache(_Context.getDefaultClassLoader())); @@ -107,14 +120,14 @@ public boolean isNamed(final Class type) { return classModel(type).head().named()!=null; } - public void setNamed(Class beanClass, String beanDefinitionName) { + public void setSpringNamed(Class beanClass, String beanDefinitionName) { _Assert.assertNotEmpty(beanDefinitionName); - classModel(beanClass).attributeMap().put("NAMED", beanDefinitionName); + classModel(beanClass).head().attributeMap().put(Attribute.SPRING_NAMED, beanDefinitionName); } public String getLogicalName(final Class type) { var model = classModel(type); - return Optional.ofNullable(model.attributeMap().get("NAMED")) + return Optional.ofNullable(model.head().attributeMap().get(Attribute.SPRING_NAMED)) .or(()->Optional.ofNullable(model.head().named())) .or(()->Optional.ofNullable(type.getCanonicalName())) .orElseGet(type::getName); @@ -244,49 +257,47 @@ public Stream streamDeclaredMethodsHaving( } } - // -- ATTRIBUTES - - public _ClassCache setAttribute( - final Class type, - final String attributeName, - final String value) { - var classModel = classModel(type); - classModel.attributeMap.put(attributeName, value); - return this; - } - - public Optional lookupAttribute( - final Class type, - final String attributeName) { - var classModel = classModel(type); - return Optional.ofNullable(classModel.attributeMap.get(attributeName)); - } - // -- IMPLEMENATION DETAILS public record ClassModelHead( MergedAnnotations mergedAnnotations, /** explicit name if any */ - @Nullable String named) { + @Nullable String named, + Map attributeMap) { static ClassModelHead create(final Class type) { var mergedAnnotations = MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY); - return new ClassModelHead(mergedAnnotations, _ClassCacheUtil.inferName(type, mergedAnnotations)); + return new ClassModelHead(mergedAnnotations, _ClassCacheUtil.inferName(type, mergedAnnotations), + new ConcurrentHashMap<>()); + } + + /** + * @see MergedAnnotations#get(Class) + */ + public MergedAnnotation annotation(Class annotationType) { + return mergedAnnotations.get(annotationType); + } + + /** + * whether type is annotated with annotationType + */ + public boolean hasAnnotation(Class annotationType) { + return mergedAnnotations.get(annotationType).isPresent(); } /** * whether type is annotated with {@link BeanInternal} or {@link Configuration} */ public boolean hasInternalBeanSemantics() { - return mergedAnnotations.get(BeanInternal.class).isPresent() - || mergedAnnotations.get(Configuration.class).isPresent(); + return hasAnnotation(BeanInternal.class) + || hasAnnotation(Configuration.class); } /** * whether type is annotated with {@link XmlRootElement} */ public boolean hasJaxbRootElementSemantics() { - return mergedAnnotations.get(XmlRootElement.class).isPresent(); + return hasAnnotation(XmlRootElement.class); } /** @@ -308,10 +319,8 @@ private record ClassModel( Map resolvedMethodsByKey, Map publicMethodsByKey, Map postConstructMethodsByKey, - - Map> declaredMethodsByAttribute, - Map attributeMap - ) { + + Map> declaredMethodsByAttribute) { ClassModel( ClassModelHead head, @@ -319,16 +328,14 @@ private record ClassModel( this(head, declaredFields, new HashMap<>(), new HashMap<>(), new HashMap<>(), - new HashMap<>(), new HashMap<>(), new HashMap<>(), - new ConcurrentHashMap<>()); + new HashMap<>(), new HashMap<>(), new HashMap<>()); } public static ClassModel headOnly(ClassModelHead head) { return new ClassModel(head, Can.empty(), Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), - Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), - Collections.emptyMap()); + Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap()); } } diff --git a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanTypeClassifier.java b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanTypeClassifier.java index 43deaa3b753..f00e44e578c 100644 --- a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanTypeClassifier.java +++ b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanTypeClassifier.java @@ -20,7 +20,6 @@ import java.io.Serializable; import java.lang.reflect.Modifier; -import java.util.Optional; import java.util.function.Supplier; import jakarta.persistence.Entity; @@ -50,27 +49,6 @@ public record CausewayBeanTypeClassifier( @NonNull Can activeProfiles, @NonNull _ClassCache classCache) { - public enum Attributes { - /** - * Corresponds to presence of a {@link DomainService} annotation. - * @see _ClassCache#lookupAttribute(Class, String) - */ - HAS_DOMAIN_SERVICE_SEMANTICS, - - /** - * Corresponds to {@link DomainObject#mixinMethod()}. - * @see _ClassCache#lookupAttribute(Class, String) - */ - MIXIN_MAIN_METHOD_NAME; - - public void set(final _ClassCache classCache, final Class type, final String attributeValue) { - classCache.setAttribute(type, this.name(), attributeValue); - } - public Optional lookup(final _ClassCache classCache, final Class type) { - return classCache.lookupAttribute(type, this.name()); - } - } - // -- CONSTRUCTION CausewayBeanTypeClassifier(final ApplicationContext applicationContext) { @@ -133,43 +111,37 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin return CausewayBeanMetaData.notManaged(discoveredBy, BeanSort.VETOED, named.get()); // reject } - // handle value types ... - - var aValue = _Annotations.synthesize(type, org.apache.causeway.applib.annotation.Value.class) - .orElse(null); - if(aValue!=null) { - return CausewayBeanMetaData.notManaged(discoveredBy, BeanSort.VALUE, named.get()); + //[CAUSEWAY-3585] when implements ViewModel, then don't consider alternatives, yield VIEW_MODEL + if(org.apache.causeway.applib.ViewModel.class.isAssignableFrom(type)) { + return CausewayBeanMetaData.causewayManaged(discoveredBy, BeanSort.VIEW_MODEL, named.get()); } - // handle internal bean types ... + var typeHead = classCache().head(type); - if(classCache.head(type).hasInternalBeanSemantics()) { + // value types + if(typeHead.hasAnnotation(org.apache.causeway.applib.annotation.Value.class)) { + return CausewayBeanMetaData.notManaged(discoveredBy, BeanSort.VALUE, named.get()); + } + + // internal bean types + if(typeHead.hasInternalBeanSemantics()) { return CausewayBeanMetaData.springManaged(discoveredBy, BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING, logicalType); } - // handle actual bean types ... - - var aDomainService = _Annotations.synthesize(type, DomainService.class); - if(aDomainService.isPresent()) { - Attributes.HAS_DOMAIN_SERVICE_SEMANTICS.set(classCache, type, "true"); + // domain service + if(typeHead.hasAnnotation(DomainService.class)) { return CausewayBeanMetaData.springManaged(discoveredBy, BeanSort.MANAGED_BEAN_CONTRIBUTING, logicalType); } - //[CAUSEWAY-3585] when implements ViewModel, then don't consider alternatives, yield VIEW_MODEL - if(org.apache.causeway.applib.ViewModel.class.isAssignableFrom(type)) { - return CausewayBeanMetaData.causewayManaged(discoveredBy, BeanSort.VIEW_MODEL, named.get()); - } - - // JDO entity support - if(classCache.head(type).isJdoPersistenceCapable()){ + // entity support + if(typeHead.isJdoPersistenceCapable()){ return CausewayBeanMetaData.entity(discoveredBy, PersistenceStack.JDO, named.get()); } - - var entityAnnotation = _Annotations.synthesize(type, Entity.class).orElse(null); - if(entityAnnotation!=null) { + if(typeHead.hasAnnotation(Entity.class)) { return CausewayBeanMetaData.entity(discoveredBy, PersistenceStack.JPA, named.get()); - } - + } + + // domain object var aDomainObject = _Annotations.synthesize(type, DomainObject.class).orElse(null); if(aDomainObject!=null) { switch (aDomainObject.nature()) { @@ -177,7 +149,7 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin return CausewayBeanMetaData.unspecified(discoveredBy, BeanSort.MANAGED_BEAN_CONTRIBUTING, named.get()); case MIXIN: // memoize mixin main name - Attributes.MIXIN_MAIN_METHOD_NAME.set(classCache, type, aDomainObject.mixinMethod()); + typeHead.attributeMap().put(_ClassCache.Attribute.MIXIN_MAIN_METHOD_NAME, aDomainObject.mixinMethod()); return CausewayBeanMetaData.causewayManaged(discoveredBy, BeanSort.MIXIN, named.get()); case ENTITY: return CausewayBeanMetaData.entity(discoveredBy, PersistenceStack.UNSPECIFIED, named.get()); @@ -188,11 +160,11 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin } } - if(_ClassCache.getInstance().head(type).hasJaxbRootElementSemantics()) { + if(typeHead.hasJaxbRootElementSemantics()) { return CausewayBeanMetaData.causewayManaged(discoveredBy, BeanSort.VIEW_MODEL, named.get()); } - if(_Annotations.isPresent(type, Component.class)) { + if(typeHead.hasAnnotation(Component.class)) { return CausewayBeanMetaData.unspecified(discoveredBy, BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING, logicalType); } diff --git a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayComponentCollector.java b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayComponentCollector.java index fc9e14c5457..0ded0d72a3f 100644 --- a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayComponentCollector.java +++ b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayComponentCollector.java @@ -71,13 +71,13 @@ void collect(final String beanDefinitionName) { case UNSPECIFIED -> { if(isRenamed) { //rename(beanDefinition, beanDefinitionName, typeMeta.logicalType().logicalName()); //TODO does not work yet - _ClassCache.getInstance().setNamed(beanClass, beanDefinitionName); + _ClassCache.getInstance().setSpringNamed(beanClass, beanDefinitionName); } } case SPRING -> { if(isRenamed) { // renaming not allowed, report back to class-cache - _ClassCache.getInstance().setNamed(beanClass, beanDefinitionName); + _ClassCache.getInstance().setSpringNamed(beanClass, beanDefinitionName); } } } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/ignore/annotation/RemoveAnnotatedMethodsFacetFactory.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/ignore/annotation/RemoveAnnotatedMethodsFacetFactory.java index ff512a37b87..2d8d9dab8e5 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/ignore/annotation/RemoveAnnotatedMethodsFacetFactory.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/ignore/annotation/RemoveAnnotatedMethodsFacetFactory.java @@ -23,8 +23,8 @@ import jakarta.inject.Inject; import org.apache.causeway.commons.internal.functions._Predicates; +import org.apache.causeway.commons.internal.reflection._ClassCache.Attribute; import org.apache.causeway.commons.internal.reflection._GenericResolver.ResolvedMethod; -import org.apache.causeway.core.config.beans.CausewayBeanTypeClassifier.Attributes; import org.apache.causeway.core.config.progmodel.ProgrammingModelConstants; import org.apache.causeway.core.metamodel.context.MetaModelContext; import org.apache.causeway.core.metamodel.facetapi.FeatureType; @@ -100,8 +100,7 @@ private Predicate isMixinMainMethod(final @NonNull ProcessClassC } // lookup attribute from class-cache as it should have been already processed by the BeanTypeClassifier var cls = processClassContext.getCls(); - var mixinMainMethodName = Attributes.MIXIN_MAIN_METHOD_NAME.lookup(getClassCache(), cls) - .orElse(null); + var mixinMainMethodName = getClassCache().head(cls).attributeMap().get(Attribute.MIXIN_MAIN_METHOD_NAME); return method->method.name().equals(mixinMainMethodName); } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java index 4f7a8f09b31..a3ba504ac6f 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java @@ -27,6 +27,7 @@ import org.springframework.lang.Nullable; import org.apache.causeway.applib.Identifier; +import org.apache.causeway.applib.annotation.DomainService; import org.apache.causeway.applib.annotation.Introspection.IntrospectionPolicy; import org.apache.causeway.commons.collections.Can; import org.apache.causeway.commons.collections.ImmutableEnumSet; @@ -41,7 +42,6 @@ import org.apache.causeway.commons.internal.reflection._MethodFacades.MethodFacade; import org.apache.causeway.commons.internal.reflection._Reflect; import org.apache.causeway.core.config.beans.CausewayBeanMetaData; -import org.apache.causeway.core.config.beans.CausewayBeanTypeClassifier.Attributes; import org.apache.causeway.core.metamodel.commons.ToString; import org.apache.causeway.core.metamodel.context.MetaModelContext; import org.apache.causeway.core.metamodel.facetapi.FacetHolder; @@ -353,9 +353,7 @@ public boolean isInjectable() { } private _Lazy isDomainServiceLazy = _Lazy.threadSafe(()-> - Attributes.HAS_DOMAIN_SERVICE_SEMANTICS.lookup(_ClassCache.getInstance(), getCorrespondingClass()) - .map("true"::equals) - .orElse(false)); + _ClassCache.getInstance().head(getCorrespondingClass()).hasAnnotation(DomainService.class)); @Override public boolean isDomainService() {