From d990433d3d3fb1bc4951cca5071eff227e59f029 Mon Sep 17 00:00:00 2001 From: Andi Huber Date: Wed, 13 Nov 2024 14:42:21 +0100 Subject: [PATCH 1/6] CAUSEWAY-2297: removes recently introduced BeanInternal --- .../applib/annotation/Programmatic.java | 2 +- commons/src/main/java/module-info.java | 1 - .../internal/annotations/BeanInternal.java | 47 ------------------- 3 files changed, 1 insertion(+), 49 deletions(-) delete mode 100644 commons/src/main/java/org/apache/causeway/commons/internal/annotations/BeanInternal.java diff --git a/api/applib/src/main/java/org/apache/causeway/applib/annotation/Programmatic.java b/api/applib/src/main/java/org/apache/causeway/applib/annotation/Programmatic.java index 81d3bd633c8..2c175cb01d3 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/annotation/Programmatic.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/annotation/Programmatic.java @@ -26,7 +26,7 @@ import java.lang.annotation.Target; /** - * Indicates that an property, collection or action is to be called + * Indicates that a property, collection or action is to be called * programmatically and should be ignored from the metamodel. * *

diff --git a/commons/src/main/java/module-info.java b/commons/src/main/java/module-info.java index d5b114ad131..88e426e4f8f 100644 --- a/commons/src/main/java/module-info.java +++ b/commons/src/main/java/module-info.java @@ -29,7 +29,6 @@ exports org.apache.causeway.commons.tabular; // internals exported as well exports org.apache.causeway.commons.internal; - exports org.apache.causeway.commons.internal.annotations; exports org.apache.causeway.commons.internal.assertions; exports org.apache.causeway.commons.internal.base; exports org.apache.causeway.commons.internal.binding; diff --git a/commons/src/main/java/org/apache/causeway/commons/internal/annotations/BeanInternal.java b/commons/src/main/java/org/apache/causeway/commons/internal/annotations/BeanInternal.java deleted file mode 100644 index ca7e453477e..00000000000 --- a/commons/src/main/java/org/apache/causeway/commons/internal/annotations/BeanInternal.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.causeway.commons.internal.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; - -/** - * Indicates that the class is a Spring managed bean, - * intended for framework internal use. - *

- * The specification-loader should skip introspection of those types. - * However, those types are considered to be injectable. - * Hence the service registry should be aware of those. - *

- * {@link BeanInternal} intentionally is NOT meta annotated with {@link Component}, - * to specifically allow for the light weight use case - * of instances being generated by {@link Bean} annotated factory methods. - */ -@Inherited -@Target({ ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface BeanInternal { - -} From 858fc3a791ee29fcdeb64e0e1b112948e6548e83 Mon Sep 17 00:00:00 2001 From: Andi Huber Date: Wed, 13 Nov 2024 15:04:45 +0100 Subject: [PATCH 2/6] CAUSEWAY-2297: rethinking BeanSort --- .../applib/services/metamodel/BeanSort.java | 165 ++++++++++-------- .../internal/reflection/_ClassCache.java | 62 +++++-- .../CausewayBeanFactoryPostProcessor.java | 12 ++ .../config/beans/CausewayBeanMetaData.java | 76 +++++++- .../beans/CausewayBeanTypeClassifier.java | 42 ++--- .../beans/CausewayBeanTypeRegistry.java | 16 +- .../beans/CausewayComponentCollector.java | 4 +- .../progmodel/ProgrammingModelConstants.java | 39 ++++- .../_testing/MetaModelContext_forTesting.java | 2 +- .../metamodel/commons/MetaModelVisitor.java | 2 +- .../metamodel/context/MetaModelContext.java | 4 +- .../object/_ManagedObjectService.java | 9 + .../objectmanager/ObjectManager.java | 4 +- .../memento/ObjectDementifierFactory.java | 2 +- .../all/SanityChecksValidator.java | 4 +- .../ChoicesAndDefaultsPostProcessor.java | 4 +- .../ClassSubstitutorAbstract.java | 2 +- .../ClassSubstitutorForDomainObjects.java | 12 ++ .../wrapper/WrapperFactoryDefault.java | 2 +- .../viewer/wicket/ui/pages/PageAbstract.java | 2 +- 20 files changed, 314 insertions(+), 151 deletions(-) diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java b/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java index 21959d10f9f..2b507155b45 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java @@ -18,9 +18,14 @@ */ package org.apache.causeway.applib.services.metamodel; +import javax.imageio.spi.ServiceRegistry; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Profile; import org.apache.causeway.applib.annotation.Programmatic; +import org.apache.causeway.applib.services.factory.FactoryService; /** * Top level object classification. @@ -34,7 +39,7 @@ public enum BeanSort { * Includes classes annotated with {@code @DomainObject}, when *not* associated * with a persistence layer.

see also {@link #ENTITY} */ - VIEW_MODEL, + VIEW_MODEL(new BeanPolicy(BeanPolicy.INTROSPECTION | BeanPolicy.WRAPPING | BeanPolicy.ELEMENTTYPE | BeanPolicy.NON_SUBSTITUTABLE)), /** * Persistable object, associated with a persistence layer/context. *

@@ -42,110 +47,130 @@ public enum BeanSort { * with a persistence layer.

see also {@link #VIEW_MODEL} * */ - ENTITY, + ENTITY(new BeanPolicy(BeanPolicy.INTROSPECTION | BeanPolicy.WRAPPING | BeanPolicy.ELEMENTTYPE | BeanPolicy.NON_SUBSTITUTABLE)), /** * Injectable object, associated with a lifecycle context * (application-scoped, request-scoped, ...). *

+ * In other words: Indicates that the class is a Spring managed bean, + * which IS also contributing to the UI or WEB API. Hence needs introspection. + *

+ * The {@link ServiceRegistry} must be aware. + *

* to be introspected: YES */ - MANAGED_BEAN_CONTRIBUTING, + MANAGED_BEAN_CONTRIBUTING(new BeanPolicy(BeanPolicy.INTROSPECTION | BeanPolicy.INJECTABLE | BeanPolicy.WRAPPING | BeanPolicy.NON_SUBSTITUTABLE)), /** * Injectable object, associated with a lifecycle context * (application-scoped, request-scoped, ...). *

+ * In other words: Indicates that the class is a Spring managed bean, + * which is NOT contributing to the UI or WEB API. Hence not introspected. + *

+ * The {@link ServiceRegistry} must be aware regardless. + *

+ * This is also the fallback {@link BeanSort} for beans originating from {@link Bean} annotated factory methods, + * when the bean class itself declares no annotations. + *

* to be introspected: NO */ - MANAGED_BEAN_NOT_CONTRIBUTING, + MANAGED_BEAN_NOT_CONTRIBUTING(new BeanPolicy(BeanPolicy.INJECTABLE | BeanPolicy.WRAPPING | BeanPolicy.NON_SUBSTITUTABLE)), /** * Object associated with an entity, viewmodel or domain-service * to act as contributer of a single domain-action or * domain-property or domain-collection. */ - MIXIN, + MIXIN(new BeanPolicy(BeanPolicy.INTROSPECTION | BeanPolicy.WRAPPING)), /** * Immutable, serializable object. + * Values (including enums) my have object support methods, hence needs introspection. */ - VALUE, + VALUE(new BeanPolicy(BeanPolicy.INTROSPECTION | BeanPolicy.ELEMENTTYPE)), /** * Container of objects. */ - COLLECTION, + COLLECTION(new BeanPolicy(0)), /** - * A non concrete type, that is a placeholder for a its concrete implementer. + * A non concrete type, that is a placeholder for a concrete implementer. + *

+ * E.g. action return types or collection element types could be interfaces or abstract types + * (as discovered by reflection during introspection) */ - ABSTRACT, + ABSTRACT(new BeanPolicy(BeanPolicy.INTROSPECTION | BeanPolicy.ELEMENTTYPE)), + /** + * Type must NOT be added to the meta-model, eg. by means of + * {@link org.apache.causeway.applib.annotation.Domain.Exclude} or {@link Programmatic}. + * Consequently the specification-loader should skip introspection of those types. + *

+ * In other words: Indicates that the class is some bean, + * which is NOT contributing to the UI or WEB API, and + * also NOT managed by Spring. + *

+ * {@link ServiceRegistry} will NOT be aware. + *

+ * {@link FactoryService#create(Class)} will nevertheless support + * those programmatic beans. They may have injection points that need resolving. + */ + PROGRAMMATIC(new BeanPolicy(BeanPolicy.WRAPPING)), /** * Type must not be added to the meta-model, eg. by means of - * {@link org.apache.causeway.applib.annotation.Domain.Exclude}, {@link Profile} or {@link Programmatic} + * {@link javax.enterprise.inject.Vetoed} or {@link Profile}. + * Consequently the specification-loader should skip introspection of those types. + * If discovered by Spring during class-path scanning, we remove the corresponding {@link BeanDefinition}. + *

+ * {@link ServiceRegistry} must not be aware of those types. + *

+ * {@link FactoryService#getOrCreate(Class)} must fail for those types. */ - VETOED, - UNKNOWN; + @SuppressWarnings("javadoc") + VETOED(new BeanPolicy(0)), + UNKNOWN(new BeanPolicy(BeanPolicy.ELEMENTTYPE)); + + public record BeanPolicy(int flags) { + final static int INTROSPECTION = 0x01; + final static int INJECTABLE = 0x02; + final static int WRAPPING = 0x04; + final static int ELEMENTTYPE = 0x08; + final static int NON_SUBSTITUTABLE = 0x10; + /** + * Enables meta-model introspection. + * Potentially contributes to the UI or Web API. + */ + public boolean isIntrospectionAllowed() { return (flags & INTROSPECTION) !=0; } + /** + * Contributes actions, members and/or object support to the UI or Web API. + */ + public boolean contributesToUiOrWebApi() { return isIntrospectionAllowed(); } // seems to be just a synonym + public boolean isInjectable() { return (flags & INJECTABLE) !=0; } + public boolean isWrappingSupported() { return (flags & WRAPPING) !=0; } + public boolean isAllowedAsMemberElementType() { return (flags & ELEMENTTYPE) !=0; } + public boolean isNotSubstitutable() { return (flags & NON_SUBSTITUTABLE) !=0; } + } - // -- SIMPLE PREDICATES + // -- POLICY - public boolean isManagedBeanContributing() { - return this == MANAGED_BEAN_CONTRIBUTING; - } + private final BeanPolicy policy; + private BeanSort(final BeanPolicy policy) { this.policy = policy;} + public BeanPolicy policy() { return policy; } - public boolean isManagedBeanNotContributing() { - return this == MANAGED_BEAN_NOT_CONTRIBUTING; - } + // -- PREDICATES + + public boolean isManagedBeanContributing() { return this == MANAGED_BEAN_CONTRIBUTING; } + public boolean isManagedBeanNotContributing() { return this == MANAGED_BEAN_NOT_CONTRIBUTING; } + public boolean isMixin() { return this == MIXIN; } + public boolean isViewModel() { return this == VIEW_MODEL; } + public boolean isValue() { return this == VALUE; } + public boolean isCollection() { return this == COLLECTION; } + public boolean isEntity() { return this == ENTITY; } + public boolean isAbstract() { return this == ABSTRACT; } + public boolean isProgrammatic() { return this == PROGRAMMATIC; } + public boolean isVetoed() { return this == VETOED; } + public boolean isUnknown() { return this == UNKNOWN; } + @Deprecated public boolean isManagedBeanAny() { return this == MANAGED_BEAN_CONTRIBUTING || this == MANAGED_BEAN_NOT_CONTRIBUTING; } - public boolean isMixin() { - return this == MIXIN; - } - - public boolean isViewModel() { - return this == VIEW_MODEL; - } - - public boolean isValue() { - return this == VALUE; - } - - public boolean isCollection() { - return this == COLLECTION; - } - - public boolean isEntity() { - return this == ENTITY; - } - - public boolean isAbstract() { - return this == ABSTRACT; - } - - public boolean isVetoed() { - return this == VETOED; - } - - public boolean isUnknown() { - return this == UNKNOWN; - } - - // -- HIGHER LEVEL PREDICATES - - public boolean isToBeIntrospected() { - - if(isVetoed() - || isUnknown() - || isCollection() - || this == MANAGED_BEAN_NOT_CONTRIBUTING) { - return false; - } - - return true; - } - - public boolean isWrappingSupported() { - return isMixin() || isViewModel() || isEntity() || isManagedBeanAny(); - } - - // ... } 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 d5caeba430c..95340488015 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 @@ -38,7 +38,7 @@ import jakarta.xml.bind.annotation.XmlRootElement; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.lang.Nullable; @@ -46,7 +46,6 @@ import org.apache.causeway.commons.collections.Can; import org.apache.causeway.commons.functional.Try; import org.apache.causeway.commons.internal._Constants; -import org.apache.causeway.commons.internal.annotations.BeanInternal; import org.apache.causeway.commons.internal.assertions._Assert; import org.apache.causeway.commons.internal.base._Casts; import org.apache.causeway.commons.internal.base._Lazy; @@ -60,6 +59,8 @@ import org.apache.causeway.commons.semantics.AccessorSemantics; import lombok.AccessLevel; +import lombok.Builder; +import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; @@ -81,6 +82,24 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class _ClassCache implements AutoCloseable { + @Builder(builderMethodName = "builderPlain") + public record Options( + @Nullable ClassLoader classLoader, + @NonNull Can> annotationsVetoingIntrospection) { + public static OptionsBuilder builder() { + return builderPlain() + .classLoader(_Context.getDefaultClassLoader()) + .annotationsVetoingIntrospection(Can.empty()); + } + public void install() { + _Context.put(_ClassCache.Options.class, this, true); + } + static Options fallback() { return builder().build(); } + static Options getInstance() { + return _Context.computeIfAbsent(Options.class, Options::fallback); + } + } + public enum Attribute { /** * Corresponds to the bean name of Spring managed beans. @@ -93,17 +112,17 @@ public enum Attribute { } public static _ClassCache getInstance() { - return _Context.computeIfAbsent(_ClassCache.class, ()->new _ClassCache(_Context.getDefaultClassLoader())); + return _Context.computeIfAbsent(_ClassCache.class, ()->new _ClassCache(Options.getInstance())); } /** * JUnit support. */ public static void invalidate() { - _Context.put(_ClassCache.class, new _ClassCache(_Context.getDefaultClassLoader()), true); + _Context.put(_ClassCache.class, new _ClassCache(Options.getInstance()), true); } - private final @Nullable ClassLoader classLoader; + private final Options options; public void add(final Class type) { classModel(type); @@ -261,11 +280,19 @@ public record ClassModelHead( MergedAnnotations mergedAnnotations, /** explicit name if any */ @Nullable String named, + /** + * if true, type is NOT contributing to the UI or WEB API, + * consequently will NOT introspect + */ + boolean hasIntrospectionVetoingSemantics, Map attributeMap) { - static ClassModelHead create(final Class type) { + static ClassModelHead create(final Class type, final Options options) { var mergedAnnotations = MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY); - return new ClassModelHead(mergedAnnotations, _ClassCacheUtil.inferName(type, mergedAnnotations), + return new ClassModelHead(mergedAnnotations, + _ClassCacheUtil.inferName(type, mergedAnnotations), + options.annotationsVetoingIntrospection() + .anyMatch(annotationType->mergedAnnotations.get(annotationType).isPresent()), new ConcurrentHashMap<>()); } @@ -281,14 +308,6 @@ public boolean hasAnnotation(final Class annotationType) { return mergedAnnotations.get(annotationType).isPresent(); } - /** - * whether type is annotated with {@link BeanInternal} or {@link Configuration} - */ - public boolean hasInternalBeanSemantics() { - return hasAnnotation(BeanInternal.class) - || hasAnnotation(Configuration.class); - } - /** * whether type is annotated with {@link XmlRootElement} */ @@ -304,6 +323,12 @@ public boolean isJdoPersistenceCapable() { && !_ClassCacheUtil.isJdoEmbeddedOnly(mergedAnnotations); } + public Can springProfiles() { + var profileAnnot = mergedAnnotations.get(Profile.class); + if(!profileAnnot.isPresent()) return Can.empty(); + return Can.ofArray(profileAnnot.getStringArray("value")); + } + } private record ClassModelBody( @@ -329,7 +354,8 @@ private record ClassModelBody( Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap()); private static ClassModelBody create(final Class type, final ClassModelHead head) { - if(head.hasInternalBeanSemantics()) return EMPTY; //skip further inspection + + if(head.hasIntrospectionVetoingSemantics) return EMPTY; //skip further inspection var body = new ClassModelBody(Can.ofArray(type.getDeclaredFields())); @@ -457,7 +483,7 @@ private ClassModel classModel(final Class type) { */ @SuppressWarnings({ "unchecked", "rawtypes" }) private Class reloadType(final Class _type) { - return Optional.ofNullable(classLoader) + return Optional.ofNullable(options.classLoader()) .filter(cl->!_type.isPrimitive()) .filter(cl->!_Reflect.isJavaApiClass(_type)) .filter(cl->!cl.equals(_type.getClassLoader())) @@ -469,7 +495,7 @@ private Class reloadType(final Class _type) { private ClassModel inspectType(final Class _type) { final Class type = reloadType(_type); - var head = ClassModelHead.create(type); + var head = ClassModelHead.create(type, options); return new ClassModel(head, _Lazy.threadSafe(()->ClassModelBody.create(type, head))); } diff --git a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanFactoryPostProcessor.java b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanFactoryPostProcessor.java index d31888f7152..02caf8457b7 100644 --- a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanFactoryPostProcessor.java +++ b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanFactoryPostProcessor.java @@ -39,8 +39,10 @@ import org.apache.causeway.commons.collections.Can; import org.apache.causeway.commons.internal.base._NullSafe; import org.apache.causeway.commons.internal.base._Timing; +import org.apache.causeway.commons.internal.reflection._ClassCache; import org.apache.causeway.core.config.CausewayModuleCoreConfig; import org.apache.causeway.core.config.beans.aoppatch.AopPatch; +import org.apache.causeway.core.config.progmodel.ProgrammingModelConstants; import lombok.extern.log4j.Log4j2; @@ -72,6 +74,7 @@ public class CausewayBeanFactoryPostProcessor @Override public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException { this.causewayBeanTypeClassifier = new CausewayBeanTypeClassifier(applicationContext); + installClassCashOptions(); } @Override @@ -115,4 +118,13 @@ public CausewayBeanTypeRegistry getComponentScanResult() { return new CausewayBeanTypeRegistry(componentScanResult); } + private void installClassCashOptions() { + _ClassCache.Options.builder() + .annotationsVetoingIntrospection( + Can.ofArray(ProgrammingModelConstants.TypeProgrammaticMarker.values()) + .map(ProgrammingModelConstants.TypeProgrammaticMarker::getAnnotationType)) + .build() + .install(); + } + } diff --git a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java index 5c5fbc09487..b3bfe0d1d69 100644 --- a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java +++ b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java @@ -31,7 +31,7 @@ public record CausewayBeanMetaData( @NonNull BeanSort beanSort, @NonNull DiscoveredBy discoveredBy, @NonNull ManagedBy managedBy, - @NonNull PersistenceStack persistenceStack) + @NonNull PersistenceStack persistenceStack) implements Serializable { public enum PersistenceStack { @@ -51,14 +51,14 @@ public String titleCase() { return _Strings.capitalize(_Strings.lower(name())); } } - + public enum DiscoveredBy { CAUSEWAY, SPRING; boolean isCauseway() { return this == CAUSEWAY; } boolean isSpring() { return this == SPRING; } } - + public enum ManagedBy { NONE, CAUSEWAY, @@ -72,7 +72,7 @@ public enum ManagedBy { public boolean isPersistence() { return this == PERSISTENCE; } public boolean isUnspecified() { return this == UNSPECIFIED; } } - + public Class getCorrespondingClass() { return logicalType.correspondingClass(); } @@ -83,6 +83,64 @@ public String getBeanName() { // -- FACTORIES + public static CausewayBeanMetaData vetoed( + final @NonNull LogicalType logicalType, + final @NonNull DiscoveredBy discoveredBy) { + return new CausewayBeanMetaData(logicalType, BeanSort.VETOED, discoveredBy, ManagedBy.NONE, PersistenceStack.NONE); + } + public static CausewayBeanMetaData value( + final @NonNull LogicalType logicalType, + final @NonNull DiscoveredBy discoveredBy) { + return new CausewayBeanMetaData(logicalType, BeanSort.VALUE, discoveredBy, ManagedBy.UNSPECIFIED, PersistenceStack.NONE); + } + public static CausewayBeanMetaData collection( + final @NonNull LogicalType logicalType, + final @NonNull DiscoveredBy discoveredBy) { + return new CausewayBeanMetaData(logicalType, BeanSort.COLLECTION, discoveredBy, ManagedBy.UNSPECIFIED, PersistenceStack.NONE); + } + public static CausewayBeanMetaData interfaceOrAbstract( + final @NonNull LogicalType logicalType, + final @NonNull DiscoveredBy discoveredBy) { + return new CausewayBeanMetaData(logicalType, BeanSort.ABSTRACT, discoveredBy, ManagedBy.UNSPECIFIED, PersistenceStack.NONE); + } + public static CausewayBeanMetaData viewModel( + final @NonNull LogicalType logicalType, + final @NonNull DiscoveredBy discoveredBy) { + return new CausewayBeanMetaData(logicalType, BeanSort.VIEW_MODEL, discoveredBy, ManagedBy.CAUSEWAY, PersistenceStack.NONE); + } + public static CausewayBeanMetaData entity( + final @NonNull LogicalType logicalType, + final @NonNull DiscoveredBy discoveredBy, + final @NonNull PersistenceStack persistenceStack) { + return new CausewayBeanMetaData(logicalType, BeanSort.ENTITY, discoveredBy, ManagedBy.PERSISTENCE, persistenceStack); + } + public static CausewayBeanMetaData mixin( + final @NonNull LogicalType logicalType, + final @NonNull DiscoveredBy discoveredBy) { + return new CausewayBeanMetaData(logicalType, BeanSort.MIXIN, discoveredBy, ManagedBy.CAUSEWAY, PersistenceStack.NONE); + } + public static CausewayBeanMetaData programmatic( + final @NonNull LogicalType logicalType) { + return new CausewayBeanMetaData(logicalType, BeanSort.PROGRAMMATIC, DiscoveredBy.CAUSEWAY, ManagedBy.CAUSEWAY, PersistenceStack.NONE); + } + public static CausewayBeanMetaData springContributing( + final @NonNull LogicalType logicalType) { + return new CausewayBeanMetaData(logicalType, BeanSort.MANAGED_BEAN_CONTRIBUTING, DiscoveredBy.SPRING, ManagedBy.SPRING, PersistenceStack.NONE); + } + public static CausewayBeanMetaData springNotContributing( + final @NonNull LogicalType logicalType) { + return new CausewayBeanMetaData(logicalType, BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING, DiscoveredBy.SPRING, ManagedBy.SPRING, PersistenceStack.NONE); + } + public static CausewayBeanMetaData unspecified( + final @NonNull LogicalType logicalType, + final @NonNull DiscoveredBy discoveredBy, + final @NonNull BeanSort beanSort) { + return new CausewayBeanMetaData(logicalType, beanSort, discoveredBy, ManagedBy.UNSPECIFIED, PersistenceStack.NONE); + } + + // -- FACTORIES + + @Deprecated public static CausewayBeanMetaData notManaged( final @NonNull DiscoveredBy discoveredBy, final @NonNull BeanSort beanSort, @@ -90,20 +148,23 @@ public static CausewayBeanMetaData notManaged( return new CausewayBeanMetaData(logicalType, beanSort, discoveredBy, ManagedBy.NONE, PersistenceStack.NONE); } + @Deprecated public static CausewayBeanMetaData causewayManaged( final @NonNull DiscoveredBy discoveredBy, final @NonNull BeanSort beanSort, final @NonNull LogicalType logicalType) { return new CausewayBeanMetaData(logicalType, beanSort, discoveredBy, ManagedBy.CAUSEWAY, PersistenceStack.NONE); } - + + @Deprecated public static CausewayBeanMetaData springManaged( final @NonNull DiscoveredBy discoveredBy, final @NonNull BeanSort beanSort, final @NonNull LogicalType logicalType) { return new CausewayBeanMetaData(logicalType, beanSort, discoveredBy, ManagedBy.SPRING, PersistenceStack.NONE); } - + + @Deprecated public static CausewayBeanMetaData entity( final @NonNull DiscoveredBy discoveredBy, final @NonNull PersistenceStack persistenceStack, @@ -112,8 +173,9 @@ public static CausewayBeanMetaData entity( } /** - * If discovered by Spring, let Spring decide whether it wants to manage this type. We do not interfere. + * If discovered by Spring, let Spring decide whether it wants to manage this type. We do not interfere. */ + @Deprecated public static CausewayBeanMetaData unspecified( final @NonNull DiscoveredBy discoveredBy, final @NonNull BeanSort beanSort, 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 35ec8ae6e05..213a4cc82bb 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 @@ -31,48 +31,48 @@ import org.apache.causeway.applib.annotation.DomainObject; import org.apache.causeway.applib.annotation.DomainService; +import org.apache.causeway.applib.annotation.Programmatic; import org.apache.causeway.applib.id.LogicalType; import org.apache.causeway.applib.services.metamodel.BeanSort; import org.apache.causeway.commons.collections.Can; -import org.apache.causeway.commons.internal.annotations.BeanInternal; import org.apache.causeway.commons.internal.reflection._Annotations; import org.apache.causeway.commons.internal.reflection._ClassCache; import org.apache.causeway.commons.semantics.CollectionSemantics; import org.apache.causeway.core.config.beans.CausewayBeanMetaData.DiscoveredBy; import org.apache.causeway.core.config.beans.CausewayBeanMetaData.PersistenceStack; -import org.apache.causeway.core.config.progmodel.ProgrammingModelConstants.TypeExcludeMarker; +import org.apache.causeway.core.config.progmodel.ProgrammingModelConstants.TypeVetoMarker; import lombok.NonNull; -@BeanInternal +@Programmatic public record CausewayBeanTypeClassifier( @NonNull Can activeProfiles, @NonNull _ClassCache classCache) { - + // -- CONSTRUCTION - + CausewayBeanTypeClassifier(final ApplicationContext applicationContext) { this(Can.ofArray(applicationContext.getEnvironment().getActiveProfiles())); } - - CausewayBeanTypeClassifier(Can activeProfiles) { + + CausewayBeanTypeClassifier(final Can activeProfiles) { this(activeProfiles, _ClassCache.getInstance()); } // -- CLASSIFY - + /** - * Classify {@link LogicalType} as detected and named by either Causeway or Spring. + * Classify {@link LogicalType} as detected and named by either Causeway or Spring. *

* Typically Causeway we will use a different fallback naming strategy for 'unnamed' types, - * that is, it uses the fully qualified class name. - * - * @param logicalType with name as either forced by Causeway or suggested by Spring + * that is, it uses the fully qualified class name. + * + * @param logicalType with name as either forced by Causeway or suggested by Spring */ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, final DiscoveredBy discoveredBy) { - + var type = logicalType.correspondingClass(); - + Supplier named = ()->discoveredBy.isSpring() ? LogicalType.infer(type) // use only if discovered by Spring but NOT managed by Spring : logicalType; // name is already inferred, when discovered by Causeway @@ -99,7 +99,7 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin } // handle vetoing ... - if(TypeExcludeMarker.anyMatchOn(type)) { + if(TypeVetoMarker.anyMatchOn(type)) { return CausewayBeanMetaData.notManaged(discoveredBy, BeanSort.VETOED, named.get()); // reject } @@ -115,16 +115,16 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin if(org.apache.causeway.applib.ViewModel.class.isAssignableFrom(type)) { return CausewayBeanMetaData.causewayManaged(discoveredBy, BeanSort.VIEW_MODEL, named.get()); } - + var typeHead = classCache().head(type); - + // 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()) { + if(typeHead.hasIntrospectionVetoingSemantics()) { return CausewayBeanMetaData.springManaged(discoveredBy, BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING, logicalType); } @@ -139,8 +139,8 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin } if(typeHead.hasAnnotation(Entity.class)) { return CausewayBeanMetaData.entity(discoveredBy, PersistenceStack.JPA, named.get()); - } - + } + // domain object var aDomainObject = typeHead.annotation(DomainObject.class).orElse(null); if(aDomainObject!=null) { diff --git a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanTypeRegistry.java b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanTypeRegistry.java index 651c091dd6c..db963dfc7ee 100644 --- a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanTypeRegistry.java +++ b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanTypeRegistry.java @@ -25,8 +25,8 @@ import java.util.Set; import java.util.stream.Stream; +import org.apache.causeway.applib.annotation.Programmatic; import org.apache.causeway.commons.collections.Can; -import org.apache.causeway.commons.internal.annotations.BeanInternal; import org.apache.causeway.core.config.beans.CausewayBeanMetaData.PersistenceStack; import lombok.NonNull; @@ -34,7 +34,7 @@ /** * Holds discovered domain types grouped by bean-sort. */ -@BeanInternal +@Programmatic public class CausewayBeanTypeRegistry { /** @@ -59,7 +59,7 @@ public CausewayBeanTypeRegistry(final @NonNull Can introsp introspectableTypes.forEach(typeMeta->{ var cls = typeMeta.getCorrespondingClass(); - + introspectableTypesByClass.put(typeMeta.getCorrespondingClass(), typeMeta); switch (typeMeta.beanSort()) { @@ -89,9 +89,9 @@ public CausewayBeanTypeRegistry(final @NonNull Can introsp } }); } - + // -- SIZE - + public int managedBeansContributingCount() { return managedBeansContributing.size(); } public int entityTypeCount() { return entityTypes.size(); } public int viewmodelTypeCount() { return viewmodelTypes.size(); } @@ -106,7 +106,7 @@ public CausewayBeanTypeRegistry(final @NonNull Can introsp public Stream> streamViewmodelTypes() { return viewmodelTypes.keySet().stream(); } public Stream> streamMixinTypes() { return mixinTypes.keySet().stream(); } public Stream> streamValueTypes() { return valueTypes.keySet().stream(); } - + // -- AS SET public Set> entityTypeSet() { return Collections.unmodifiableSet(entityTypes.keySet()); } @@ -122,8 +122,8 @@ public Optional lookupManagedBeanNameForType(final Class type) { return Optional.ofNullable(managedBeansContributing.get(type)) .map(CausewayBeanMetaData::getBeanName); } - - public boolean containsManagedBeansContributing(@NonNull Class type) { + + public boolean containsManagedBeansContributing(@NonNull final Class type) { return managedBeansContributing.containsKey(type); } 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 bd16af4db73..4c57f279eb9 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 @@ -67,7 +67,7 @@ void collect(final String beanDefinitionName) { switch (typeMeta.managedBy()) { case NONE, CAUSEWAY, PERSISTENCE -> { registry.removeBeanDefinition(beanDefinitionName); - log.debug("vetoing bean {}", beanDefinitionName); + log.debug("removing bean from spring registry {}", beanDefinitionName); } case UNSPECIFIED, SPRING -> { if(isRenamed) { @@ -95,7 +95,7 @@ void collect(final String beanDefinitionName) { private CausewayBeanMetaData collectBeanClass(final LogicalType logicalType) { var typeMeta = causewayBeanTypeClassifier.classify(logicalType, DiscoveredBy.SPRING); var beanSort = typeMeta.beanSort(); - if(beanSort.isToBeIntrospected()) { + if(beanSort.policy().isIntrospectionAllowed()) { var beanClass = logicalType.correspondingClass(); introspectableTypes.put(beanClass, typeMeta); if(log.isDebugEnabled()) { diff --git a/core/config/src/main/java/org/apache/causeway/core/config/progmodel/ProgrammingModelConstants.java b/core/config/src/main/java/org/apache/causeway/core/config/progmodel/ProgrammingModelConstants.java index 945c3239b54..a4ba48eb9c2 100644 --- a/core/config/src/main/java/org/apache/causeway/core/config/progmodel/ProgrammingModelConstants.java +++ b/core/config/src/main/java/org/apache/causeway/core/config/progmodel/ProgrammingModelConstants.java @@ -35,7 +35,9 @@ import java.util.function.IntFunction; import java.util.stream.Stream; +import org.springframework.context.annotation.Configuration; import org.springframework.lang.Nullable; +import org.springframework.stereotype.Repository; import org.apache.causeway.applib.Identifier; import org.apache.causeway.applib.annotation.Domain; @@ -52,6 +54,7 @@ import org.apache.causeway.commons.internal.exceptions._Exceptions; import org.apache.causeway.commons.internal.reflection._Annotations; import org.apache.causeway.commons.internal.reflection._ClassCache; +import org.apache.causeway.commons.internal.reflection._ClassCache.ClassModelHead; import org.apache.causeway.commons.internal.reflection._GenericResolver.ResolvedConstructor; import org.apache.causeway.commons.internal.reflection._GenericResolver.ResolvedMethod; import org.apache.causeway.commons.internal.reflection._MethodFacades.MethodFacade; @@ -67,21 +70,39 @@ public final class ProgrammingModelConstants { - // -- TYPE EXCLUDE MARKERS + // -- TYPE MARKERS @Getter @RequiredArgsConstructor - public enum TypeExcludeMarker { - DOMAIN_EXCLUDE(Domain.Exclude.class) - //,VETO(javax.enterprise.inject.Vetoed.class) + public enum TypeVetoMarker { + //VETO(javax.enterprise.inject.Vetoed.class) ; private final Class annotationType; - public static boolean anyMatchOn(final Class type) { - for(TypeExcludeMarker excludeMarker : TypeExcludeMarker.values()) { - if(_Annotations.synthesize(type, excludeMarker.getAnnotationType()).isPresent()) { - return true; - } + public static boolean anyMatchOn(final ClassModelHead typeHead) { + for(TypeVetoMarker marker : TypeVetoMarker.values()) { + if(typeHead.hasAnnotation(marker.annotationType)) return true; + } + return false; + } + + public static boolean anyMatchOn(final Class cls) { + return anyMatchOn(_ClassCache.getInstance().head(cls)); + } + } + + @Getter + @RequiredArgsConstructor + public enum TypeProgrammaticMarker { + SPRING_CONFIG(Configuration.class), + SPRING_REPOSITORY(Repository.class), + DOMAIN_EXCLUDE(Domain.Exclude.class), + ; + private final Class annotationType; + + public static boolean anyMatchOn(final ClassModelHead typeHead) { + for(TypeProgrammaticMarker marker : TypeProgrammaticMarker.values()) { + if(typeHead.hasAnnotation(marker.annotationType)) return true; } return false; } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java index e0ec4ec11f6..dce69411935 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/_testing/MetaModelContext_forTesting.java @@ -558,7 +558,7 @@ private final Map collectBeansOfKnownSort() { private Optional toServiceInstance(final _SingletonBeanProvider managedBeanAdapter) { var servicePojo = managedBeanAdapter.getInstanceElseFail(); - if(ProgrammingModelConstants.TypeExcludeMarker.anyMatchOn(managedBeanAdapter.getBeanClass())) { + if(ProgrammingModelConstants.TypeVetoMarker.anyMatchOn(managedBeanAdapter.getBeanClass())) { return Optional.empty(); } return getSpecificationLoader() diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/commons/MetaModelVisitor.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/commons/MetaModelVisitor.java index d24f8f73400..aebc1317115 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/commons/MetaModelVisitor.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/commons/MetaModelVisitor.java @@ -53,7 +53,7 @@ default boolean isEnabled() { */ public final static Predicate SKIP_ABSTRACT = spec->!spec.isAbstract() - && spec.getBeanSort().isToBeIntrospected(); + && spec.getBeanSort().policy().isIntrospectionAllowed(); /** types pass this filter, if is NOT a mixin */ public final static Predicate SKIP_MIXINS = diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MetaModelContext.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MetaModelContext.java index cacd91b0e46..4e338fe7772 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MetaModelContext.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/context/MetaModelContext.java @@ -23,15 +23,15 @@ import org.springframework.lang.Nullable; +import org.apache.causeway.applib.annotation.Programmatic; import org.apache.causeway.applib.services.i18n.TranslationService; -import org.apache.causeway.commons.internal.annotations.BeanInternal; import org.apache.causeway.commons.internal.assertions._Assert; import org.apache.causeway.commons.internal.exceptions._Exceptions; /** * @since 2.0 */ -@BeanInternal +@Programmatic public abstract class MetaModelContext implements HasMetaModelContext { @Override diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectService.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectService.java index b852235e538..94da8163cd4 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectService.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectService.java @@ -56,6 +56,15 @@ final class _ManagedObjectService this.pojo = assertCompliance(pojo); } +// _ManagedObjectService( +// final ObjectSpecification spec, +// final Object pojo) { +// super(ManagedObject.Specialization.SERVICE, spec); +// _Assert.assertTrue(spec.isInjectable(), ()->"type %s must be injectable to be considered a service; bean-sort: %s" +// .formatted(pojo.getClass(), spec.getBeanSort())); +// this.pojo = assertCompliance(pojo); +// } + @Override public Optional getBookmark() { return bookmarkLazy.get(); diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectManager.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectManager.java index 1037ded3c12..f7bc02587cc 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectManager.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/ObjectManager.java @@ -25,12 +25,12 @@ import org.springframework.lang.Nullable; +import org.apache.causeway.applib.annotation.Programmatic; import org.apache.causeway.applib.exceptions.unrecoverable.BookmarkNotFoundException; import org.apache.causeway.applib.query.Query; import org.apache.causeway.applib.services.bookmark.Bookmark; import org.apache.causeway.commons.collections.Can; import org.apache.causeway.commons.handler.ChainOfResponsibility; -import org.apache.causeway.commons.internal.annotations.BeanInternal; import org.apache.causeway.commons.internal.assertions._Assert; import org.apache.causeway.commons.internal.base._NullSafe; import org.apache.causeway.commons.internal.exceptions._Exceptions; @@ -58,7 +58,7 @@ * * @since 2.0 */ -@BeanInternal +@Programmatic @Named(CausewayModuleCoreMetamodel.NAMESPACE + ".ObjectManager") public record ObjectManager( MetaModelContext mmc, diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectDementifierFactory.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectDementifierFactory.java index 3d032f99433..28139e6ba2e 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectDementifierFactory.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/objectmanager/memento/ObjectDementifierFactory.java @@ -52,7 +52,7 @@ public ManagedObject handle(final MementoRecreateRequest request) { var spec = request.objectSpecification(); var mmc = spec.getMetaModelContext(); // intercept when managed by Spring - return spec.getBeanSort().isManagedBeanAny() + return spec.getBeanSort().policy().isInjectable() ? mmc.lookupServiceAdapterById(request.memento().logicalType().logicalName()) : mmc.getObjectManager().loadObjectElseFail(request.memento().bookmark()); } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/all/SanityChecksValidator.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/all/SanityChecksValidator.java index c6dcfed8270..d51959c2afc 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/all/SanityChecksValidator.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/all/SanityChecksValidator.java @@ -96,9 +96,7 @@ private void checkElementType( final ObjectSpecification elementType) { if(elementType == null - || elementType.getBeanSort().isManagedBeanAny() - || elementType.getBeanSort().isMixin() - || elementType.getBeanSort().isVetoed()) { + || !elementType.getBeanSort().policy().isAllowedAsMemberElementType()) { ValidationFailureUtils.raiseInvalidMemberElementType(facetHolder, declaringType, elementType); } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/param/ChoicesAndDefaultsPostProcessor.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/param/ChoicesAndDefaultsPostProcessor.java index a68d235df6e..3eadcc9e70a 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/param/ChoicesAndDefaultsPostProcessor.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/param/ChoicesAndDefaultsPostProcessor.java @@ -232,9 +232,7 @@ private static void addCollectionParamChoicesFacetIfNoneAlready( private void checkParamHasChoicesOrAutoCompleteWhenRequired(final ObjectActionParameter param) { var elementType = param.getElementType(); if(elementType == null - || elementType.getBeanSort().isManagedBeanAny() - || elementType.getBeanSort().isMixin() - || elementType.getBeanSort().isVetoed()) { + || !elementType.getBeanSort().policy().isAllowedAsMemberElementType()) { // ignore, as these cases are covered later by meta-model validation return; } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorAbstract.java index ddaabcf0cee..babf513204e 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorAbstract.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorAbstract.java @@ -128,7 +128,7 @@ private boolean shouldIgnore(final Class cls) { } // ignore vetoed types - if(ProgrammingModelConstants.TypeExcludeMarker.anyMatchOn(cls)) { + if(ProgrammingModelConstants.TypeVetoMarker.anyMatchOn(cls)) { return true; } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java index 4cb7efa50ae..651bb9fd6b2 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java @@ -60,4 +60,16 @@ public Substitution getSubstitution(@NonNull final Class cls) { return Substitution.passThrough(); // indifferent } +// @Override +// public Substitution getSubstitution(@NonNull final Class cls) { +// var notSubstitutable = causewayBeanTypeRegistry.lookupIntrospectableType(cls) +// .map(CausewayBeanMetaData::beanSort) +// .map(BeanSort::policy) +// .map(BeanPolicy::isNotSubstitutable) +// .orElse(false); +// return notSubstitutable +// ? Substitution.neverReplaceClass() +// : Substitution.passThrough(); // indifferent +// } + } diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java index 44a98da2981..67f18af9453 100644 --- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java +++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java @@ -561,7 +561,7 @@ private ManagedObject adaptAndGuardAgainstWrappingNotSupported( var adapter = getObjectManager().adapt(domainObject); if(ManagedObjects.isNullOrUnspecifiedOrEmpty(adapter) - || !adapter.getSpecification().getBeanSort().isWrappingSupported()) { + || !adapter.getSpecification().getBeanSort().policy().isWrappingSupported()) { throw _Exceptions.illegalArgument("Cannot wrap an object of type %s", domainObject.getClass().getName()); } diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/PageAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/PageAbstract.java index 42d09284787..6c4e8f74d41 100644 --- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/PageAbstract.java +++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/PageAbstract.java @@ -418,7 +418,7 @@ public ActionPrompt getActionPrompt( } var dialogMode = - sort.isManagedBeanAny() + sort.policy().isInjectable() ? getWicketViewerSettings().getDialogModeForMenu() : getWicketViewerSettings().getDialogMode(); switch (dialogMode) { From f57cf726baeff67e6fa22cfd07602df8e4f8eb50 Mon Sep 17 00:00:00 2001 From: Andi Huber Date: Wed, 13 Nov 2024 15:27:02 +0100 Subject: [PATCH 3/6] CAUSEWAY-2297: bean-sort refactoring --- .../beans/CausewayBeanTypeClassifier.java | 18 +++++----- .../beans/CausewayBeanTypeRegistry.java | 3 +- .../ClassSubstitutorForDomainObjects.java | 36 ++++++------------- 3 files changed, 22 insertions(+), 35 deletions(-) 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 213a4cc82bb..9681aa9281b 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 @@ -25,7 +25,6 @@ import jakarta.persistence.Entity; import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; import org.springframework.util.ClassUtils; @@ -35,11 +34,11 @@ import org.apache.causeway.applib.id.LogicalType; import org.apache.causeway.applib.services.metamodel.BeanSort; import org.apache.causeway.commons.collections.Can; -import org.apache.causeway.commons.internal.reflection._Annotations; import org.apache.causeway.commons.internal.reflection._ClassCache; import org.apache.causeway.commons.semantics.CollectionSemantics; import org.apache.causeway.core.config.beans.CausewayBeanMetaData.DiscoveredBy; import org.apache.causeway.core.config.beans.CausewayBeanMetaData.PersistenceStack; +import org.apache.causeway.core.config.progmodel.ProgrammingModelConstants.TypeProgrammaticMarker; import org.apache.causeway.core.config.progmodel.ProgrammingModelConstants.TypeVetoMarker; import lombok.NonNull; @@ -98,26 +97,29 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin return CausewayBeanMetaData.unspecified(discoveredBy, BeanSort.ABSTRACT, named.get()); } + var typeHead = classCache().head(type); + // handle vetoing ... - if(TypeVetoMarker.anyMatchOn(type)) { + if(TypeVetoMarker.anyMatchOn(typeHead)) { return CausewayBeanMetaData.notManaged(discoveredBy, BeanSort.VETOED, named.get()); // reject } - var profiles = Can.ofArray(_Annotations.synthesize(type, Profile.class) - .map(Profile::value) - .orElse(null)); + var profiles = typeHead.springProfiles(); if(profiles.isNotEmpty() && !profiles.stream().anyMatch(this::isProfileActive)) { return CausewayBeanMetaData.notManaged(discoveredBy, BeanSort.VETOED, named.get()); // reject } + // handle introspection veto ... + if(TypeProgrammaticMarker.anyMatchOn(typeHead)) { + return CausewayBeanMetaData.springManaged(discoveredBy, BeanSort.MANAGED_BEAN_NOT_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()); } - var typeHead = classCache().head(type); - // value types if(typeHead.hasAnnotation(org.apache.causeway.applib.annotation.Value.class)) { return CausewayBeanMetaData.notManaged(discoveredBy, BeanSort.VALUE, named.get()); diff --git a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanTypeRegistry.java b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanTypeRegistry.java index db963dfc7ee..20445e22f55 100644 --- a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanTypeRegistry.java +++ b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanTypeRegistry.java @@ -60,7 +60,7 @@ public CausewayBeanTypeRegistry(final @NonNull Can introsp var cls = typeMeta.getCorrespondingClass(); - introspectableTypesByClass.put(typeMeta.getCorrespondingClass(), typeMeta); + introspectableTypesByClass.put(cls, typeMeta); switch (typeMeta.beanSort()) { case MANAGED_BEAN_CONTRIBUTING: @@ -80,6 +80,7 @@ public CausewayBeanTypeRegistry(final @NonNull Can introsp return; // skip introspection for these + case PROGRAMMATIC: case MANAGED_BEAN_NOT_CONTRIBUTING: case COLLECTION: case ABSTRACT: // <-- unexpected code reach diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java index 651bb9fd6b2..d28a4cfce65 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorForDomainObjects.java @@ -24,6 +24,8 @@ import org.springframework.stereotype.Component; import org.apache.causeway.applib.annotation.PriorityPrecedence; +import org.apache.causeway.applib.services.metamodel.BeanSort; +import org.apache.causeway.applib.services.metamodel.BeanSort.BeanPolicy; import org.apache.causeway.core.config.beans.CausewayBeanMetaData; import org.apache.causeway.core.config.beans.CausewayBeanTypeRegistry; import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel; @@ -44,32 +46,14 @@ public ClassSubstitutorForDomainObjects(final CausewayBeanTypeRegistry causewayB @Override public Substitution getSubstitution(@NonNull final Class cls) { - - var beanSort = causewayBeanTypeRegistry.lookupIntrospectableType(cls) - .map(CausewayBeanMetaData::beanSort) - .orElse(null); - - if(beanSort!=null) { - if(beanSort.isEntity() - || beanSort.isViewModel() - || beanSort.isManagedBeanAny()) { - return Substitution.neverReplaceClass(); - } - } - - return Substitution.passThrough(); // indifferent + var notSubstitutable = causewayBeanTypeRegistry.lookupIntrospectableType(cls) + .map(CausewayBeanMetaData::beanSort) + .map(BeanSort::policy) + .map(BeanPolicy::isNotSubstitutable) + .orElse(false); + return notSubstitutable + ? Substitution.neverReplaceClass() + : Substitution.passThrough(); // indifferent } -// @Override -// public Substitution getSubstitution(@NonNull final Class cls) { -// var notSubstitutable = causewayBeanTypeRegistry.lookupIntrospectableType(cls) -// .map(CausewayBeanMetaData::beanSort) -// .map(BeanSort::policy) -// .map(BeanPolicy::isNotSubstitutable) -// .orElse(false); -// return notSubstitutable -// ? Substitution.neverReplaceClass() -// : Substitution.passThrough(); // indifferent -// } - } From 8c88008670c957278e6c4d2d3b387639160b7b31 Mon Sep 17 00:00:00 2001 From: Andi Huber Date: Wed, 13 Nov 2024 16:07:23 +0100 Subject: [PATCH 4/6] CAUSEWAY-2297: work on cleaning up classifier --- .../config/beans/CausewayBeanMetaData.java | 12 ++------- .../beans/CausewayBeanTypeClassifier.java | 27 +++++++++---------- .../object/_ManagedObjectService.java | 23 +++++----------- 3 files changed, 22 insertions(+), 40 deletions(-) diff --git a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java index b3bfe0d1d69..f9977f93fed 100644 --- a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java +++ b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java @@ -91,12 +91,12 @@ public static CausewayBeanMetaData vetoed( public static CausewayBeanMetaData value( final @NonNull LogicalType logicalType, final @NonNull DiscoveredBy discoveredBy) { - return new CausewayBeanMetaData(logicalType, BeanSort.VALUE, discoveredBy, ManagedBy.UNSPECIFIED, PersistenceStack.NONE); + return new CausewayBeanMetaData(logicalType, BeanSort.VALUE, discoveredBy, ManagedBy.NONE, PersistenceStack.NONE); } public static CausewayBeanMetaData collection( final @NonNull LogicalType logicalType, final @NonNull DiscoveredBy discoveredBy) { - return new CausewayBeanMetaData(logicalType, BeanSort.COLLECTION, discoveredBy, ManagedBy.UNSPECIFIED, PersistenceStack.NONE); + return new CausewayBeanMetaData(logicalType, BeanSort.COLLECTION, discoveredBy, ManagedBy.CAUSEWAY, PersistenceStack.NONE); } public static CausewayBeanMetaData interfaceOrAbstract( final @NonNull LogicalType logicalType, @@ -164,14 +164,6 @@ public static CausewayBeanMetaData springManaged( return new CausewayBeanMetaData(logicalType, beanSort, discoveredBy, ManagedBy.SPRING, PersistenceStack.NONE); } - @Deprecated - public static CausewayBeanMetaData entity( - final @NonNull DiscoveredBy discoveredBy, - final @NonNull PersistenceStack persistenceStack, - final @NonNull LogicalType logicalType) { - return new CausewayBeanMetaData(logicalType, BeanSort.ENTITY, discoveredBy, ManagedBy.PERSISTENCE, persistenceStack); - } - /** * If discovered by Spring, let Spring decide whether it wants to manage this type. We do not interfere. */ 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 9681aa9281b..b1fe85427be 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 @@ -78,11 +78,11 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin if(ClassUtils.isPrimitiveOrWrapper(type) || type.isEnum()) { - return CausewayBeanMetaData.notManaged(discoveredBy, BeanSort.VALUE, named.get()); + return CausewayBeanMetaData.value(named.get(), discoveredBy); } if(CollectionSemantics.valueOf(type).isPresent()) { - return CausewayBeanMetaData.causewayManaged(discoveredBy, BeanSort.COLLECTION, named.get()); + return CausewayBeanMetaData.collection(named.get(), discoveredBy); } if(type.isInterface() @@ -94,26 +94,30 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin // and should also never be identified as ENTITY, VIEWMODEL or MIXIN // however, concrete types that inherit abstract ones with vetoes, // will effectively be vetoed through means of annotation synthesis - return CausewayBeanMetaData.unspecified(discoveredBy, BeanSort.ABSTRACT, named.get()); + return CausewayBeanMetaData.interfaceOrAbstract(named.get(), discoveredBy); } var typeHead = classCache().head(type); // handle vetoing ... if(TypeVetoMarker.anyMatchOn(typeHead)) { - return CausewayBeanMetaData.notManaged(discoveredBy, BeanSort.VETOED, named.get()); // reject + return CausewayBeanMetaData.vetoed(named.get(), discoveredBy); // reject } var profiles = typeHead.springProfiles(); if(profiles.isNotEmpty() && !profiles.stream().anyMatch(this::isProfileActive)) { - return CausewayBeanMetaData.notManaged(discoveredBy, BeanSort.VETOED, named.get()); // reject + return CausewayBeanMetaData.vetoed(named.get(), discoveredBy); // reject } // handle introspection veto ... if(TypeProgrammaticMarker.anyMatchOn(typeHead)) { return CausewayBeanMetaData.springManaged(discoveredBy, BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING, logicalType); } + // programmatic bean types + if(typeHead.hasIntrospectionVetoingSemantics()) { + return CausewayBeanMetaData.springManaged(discoveredBy, BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING, logicalType); + } //[CAUSEWAY-3585] when implements ViewModel, then don't consider alternatives, yield VIEW_MODEL if(org.apache.causeway.applib.ViewModel.class.isAssignableFrom(type)) { @@ -122,12 +126,7 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin // 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.hasIntrospectionVetoingSemantics()) { - return CausewayBeanMetaData.springManaged(discoveredBy, BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING, logicalType); + return CausewayBeanMetaData.value(named.get(), discoveredBy); } // domain service @@ -137,10 +136,10 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin // entity support if(typeHead.isJdoPersistenceCapable()){ - return CausewayBeanMetaData.entity(discoveredBy, PersistenceStack.JDO, named.get()); + return CausewayBeanMetaData.entity(named.get(), discoveredBy, PersistenceStack.JDO); } if(typeHead.hasAnnotation(Entity.class)) { - return CausewayBeanMetaData.entity(discoveredBy, PersistenceStack.JPA, named.get()); + return CausewayBeanMetaData.entity(named.get(), discoveredBy, PersistenceStack.JPA); } // domain object @@ -154,7 +153,7 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin 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()); + return CausewayBeanMetaData.entity(named.get(), discoveredBy, PersistenceStack.UNSPECIFIED); case VIEW_MODEL: case NOT_SPECIFIED: //because object is not associated with a persistence context unless discovered above diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectService.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectService.java index 94da8163cd4..5c40f890fc4 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectService.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/_ManagedObjectService.java @@ -46,25 +46,10 @@ final class _ManagedObjectService final ObjectSpecification spec, final Object pojo) { super(ManagedObject.Specialization.SERVICE, spec); - _Assert.assertTrue(spec.getBeanSort().isManagedBeanAny() - || spec.getBeanSort().isUnknown(), ()-> - String.format("service %s must be a managed bean (or unknown); sort= %s", - pojo.getClass(), spec.getBeanSort())); - _Assert.assertTrue(spec.isInjectable(), ()-> - String.format("service %s must be injectible; sort= %s", - pojo.getClass(), spec.getBeanSort())); + assertInjectable(spec); this.pojo = assertCompliance(pojo); } -// _ManagedObjectService( -// final ObjectSpecification spec, -// final Object pojo) { -// super(ManagedObject.Specialization.SERVICE, spec); -// _Assert.assertTrue(spec.isInjectable(), ()->"type %s must be injectable to be considered a service; bean-sort: %s" -// .formatted(pojo.getClass(), spec.getBeanSort())); -// this.pojo = assertCompliance(pojo); -// } - @Override public Optional getBookmark() { return bookmarkLazy.get(); @@ -77,6 +62,12 @@ public boolean isBookmarkMemoized() { // -- HELPER + private void assertInjectable(final ObjectSpecification spec) { + _Assert.assertTrue(spec.isInjectable(), + ()->"type %s must be injectable to be considered a service; bean-sort: %s" + .formatted(pojo.getClass(), spec.getBeanSort())); + } + private Bookmark createBookmark() { return createBookmark("1"); } From 2330486f750b0ecea54b9fe240e091a1f212bae1 Mon Sep 17 00:00:00 2001 From: Andi Huber Date: Wed, 13 Nov 2024 16:44:12 +0100 Subject: [PATCH 5/6] CAUSEWAY-2297: work on cleaning up classifier (2) --- .../core/config/beans/CausewayBeanMetaData.java | 16 ---------------- .../config/beans/CausewayBeanTypeClassifier.java | 8 ++++---- .../specloader/SpecificationLoaderDefault.java | 3 +-- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java index f9977f93fed..bbb2ddb19c2 100644 --- a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java +++ b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java @@ -140,22 +140,6 @@ public static CausewayBeanMetaData unspecified( // -- FACTORIES - @Deprecated - public static CausewayBeanMetaData notManaged( - final @NonNull DiscoveredBy discoveredBy, - final @NonNull BeanSort beanSort, - final @NonNull LogicalType logicalType) { - return new CausewayBeanMetaData(logicalType, beanSort, discoveredBy, ManagedBy.NONE, PersistenceStack.NONE); - } - - @Deprecated - public static CausewayBeanMetaData causewayManaged( - final @NonNull DiscoveredBy discoveredBy, - final @NonNull BeanSort beanSort, - final @NonNull LogicalType logicalType) { - return new CausewayBeanMetaData(logicalType, beanSort, discoveredBy, ManagedBy.CAUSEWAY, PersistenceStack.NONE); - } - @Deprecated public static CausewayBeanMetaData springManaged( final @NonNull DiscoveredBy discoveredBy, 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 b1fe85427be..380efbf34fd 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 @@ -121,7 +121,7 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin //[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()); + return CausewayBeanMetaData.viewModel(named.get(), discoveredBy); } // value types @@ -151,18 +151,18 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin case MIXIN: // memoize mixin main name typeHead.attributeMap().put(_ClassCache.Attribute.MIXIN_MAIN_METHOD_NAME, aDomainObject.mixinMethod()); - return CausewayBeanMetaData.causewayManaged(discoveredBy, BeanSort.MIXIN, named.get()); + return CausewayBeanMetaData.mixin(named.get(), discoveredBy); case ENTITY: return CausewayBeanMetaData.entity(named.get(), discoveredBy, PersistenceStack.UNSPECIFIED); case VIEW_MODEL: case NOT_SPECIFIED: //because object is not associated with a persistence context unless discovered above - return CausewayBeanMetaData.causewayManaged(discoveredBy, BeanSort.VIEW_MODEL, named.get()); + return CausewayBeanMetaData.viewModel(named.get(), discoveredBy); } } if(typeHead.hasJaxbRootElementSemantics()) { - return CausewayBeanMetaData.causewayManaged(discoveredBy, BeanSort.VIEW_MODEL, named.get()); + return CausewayBeanMetaData.viewModel(named.get(), discoveredBy); } if(typeHead.hasAnnotation(Component.class)) { diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/SpecificationLoaderDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/SpecificationLoaderDefault.java index 2872b81911f..94e4eaa1875 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/SpecificationLoaderDefault.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/SpecificationLoaderDefault.java @@ -47,7 +47,6 @@ import org.apache.causeway.applib.annotation.SemanticsOf; import org.apache.causeway.applib.id.LogicalType; import org.apache.causeway.applib.services.menu.MenuBarsService; -import org.apache.causeway.applib.services.metamodel.BeanSort; import org.apache.causeway.applib.services.registry.ServiceRegistry; import org.apache.causeway.applib.value.semantics.ValueSemanticsResolver; import org.apache.causeway.commons.collections.Can; @@ -558,7 +557,7 @@ private CausewayBeanMetaData classify(final @Nullable Class type) { .lookupIntrospectableType(type) .orElseGet(()-> valueSemanticsResolver.get().hasValueSemantics(type) - ? CausewayBeanMetaData.causewayManaged(DiscoveredBy.CAUSEWAY, BeanSort.VALUE, LogicalType.infer(type)) + ? CausewayBeanMetaData.value(LogicalType.infer(type), DiscoveredBy.CAUSEWAY) : causewayBeanTypeClassifier.classify(LogicalType.infer(type), DiscoveredBy.CAUSEWAY) ); } From 59791d1e41af5ce7dbd61d9903b01069cc828f0c Mon Sep 17 00:00:00 2001 From: Andi Huber Date: Wed, 13 Nov 2024 18:02:02 +0100 Subject: [PATCH 6/6] CAUSEWAY-2297: work on cleaning up classifier (3) --- .../applib/services/metamodel/BeanSort.java | 5 +- .../internal/reflection/_ClassCache.java | 52 ++++--------------- .../CausewayBeanFactoryPostProcessor.java | 15 +----- .../config/beans/CausewayBeanMetaData.java | 24 ++------- .../beans/CausewayBeanTypeClassifier.java | 48 ++++++++++------- 5 files changed, 49 insertions(+), 95 deletions(-) diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java b/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java index 2b507155b45..017aa7cfbfb 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java @@ -141,6 +141,10 @@ public record BeanPolicy(int flags) { * Contributes actions, members and/or object support to the UI or Web API. */ public boolean contributesToUiOrWebApi() { return isIntrospectionAllowed(); } // seems to be just a synonym + /** + * Whether corresponding type is in principle injectable, + * that is, provided it is also registered with a Spring context. + */ public boolean isInjectable() { return (flags & INJECTABLE) !=0; } public boolean isWrappingSupported() { return (flags & WRAPPING) !=0; } public boolean isAllowedAsMemberElementType() { return (flags & ELEMENTTYPE) !=0; } @@ -167,7 +171,6 @@ public record BeanPolicy(int flags) { public boolean isVetoed() { return this == VETOED; } public boolean isUnknown() { return this == UNKNOWN; } - @Deprecated public boolean isManagedBeanAny() { return this == MANAGED_BEAN_CONTRIBUTING || this == MANAGED_BEAN_NOT_CONTRIBUTING; 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 95340488015..238ec74e44d 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 @@ -24,7 +24,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -59,8 +58,6 @@ import org.apache.causeway.commons.semantics.AccessorSemantics; import lombok.AccessLevel; -import lombok.Builder; -import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; @@ -82,24 +79,6 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class _ClassCache implements AutoCloseable { - @Builder(builderMethodName = "builderPlain") - public record Options( - @Nullable ClassLoader classLoader, - @NonNull Can> annotationsVetoingIntrospection) { - public static OptionsBuilder builder() { - return builderPlain() - .classLoader(_Context.getDefaultClassLoader()) - .annotationsVetoingIntrospection(Can.empty()); - } - public void install() { - _Context.put(_ClassCache.Options.class, this, true); - } - static Options fallback() { return builder().build(); } - static Options getInstance() { - return _Context.computeIfAbsent(Options.class, Options::fallback); - } - } - public enum Attribute { /** * Corresponds to the bean name of Spring managed beans. @@ -111,19 +90,19 @@ public enum Attribute { MIXIN_MAIN_METHOD_NAME; } + @Nullable private final ClassLoader classLoader; + private static _ClassCache defaultInstance() { return new _ClassCache(_Context.getDefaultClassLoader()); } public static _ClassCache getInstance() { - return _Context.computeIfAbsent(_ClassCache.class, ()->new _ClassCache(Options.getInstance())); + return _Context.computeIfAbsent(_ClassCache.class, ()->defaultInstance()); } /** * JUnit support. */ public static void invalidate() { - _Context.put(_ClassCache.class, new _ClassCache(Options.getInstance()), true); + _Context.put(_ClassCache.class, defaultInstance(), true); } - private final Options options; - public void add(final Class type) { classModel(type); } @@ -280,19 +259,12 @@ public record ClassModelHead( MergedAnnotations mergedAnnotations, /** explicit name if any */ @Nullable String named, - /** - * if true, type is NOT contributing to the UI or WEB API, - * consequently will NOT introspect - */ - boolean hasIntrospectionVetoingSemantics, Map attributeMap) { - static ClassModelHead create(final Class type, final Options options) { + static ClassModelHead create(final Class type) { var mergedAnnotations = MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY); return new ClassModelHead(mergedAnnotations, _ClassCacheUtil.inferName(type, mergedAnnotations), - options.annotationsVetoingIntrospection() - .anyMatch(annotationType->mergedAnnotations.get(annotationType).isPresent()), new ConcurrentHashMap<>()); } @@ -348,15 +320,13 @@ private record ClassModelBody( new HashMap<>(), new HashMap<>(), new HashMap<>()); } - private static ClassModelBody EMPTY = new ClassModelBody( - Can.empty(), - Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), - Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap()); +// private static ClassModelBody EMPTY = new ClassModelBody( +// Can.empty(), +// Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), +// Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap()); private static ClassModelBody create(final Class type, final ClassModelHead head) { - if(head.hasIntrospectionVetoingSemantics) return EMPTY; //skip further inspection - var body = new ClassModelBody(Can.ofArray(type.getDeclaredFields())); // process public constructors @@ -483,7 +453,7 @@ private ClassModel classModel(final Class type) { */ @SuppressWarnings({ "unchecked", "rawtypes" }) private Class reloadType(final Class _type) { - return Optional.ofNullable(options.classLoader()) + return Optional.ofNullable(classLoader) .filter(cl->!_type.isPrimitive()) .filter(cl->!_Reflect.isJavaApiClass(_type)) .filter(cl->!cl.equals(_type.getClassLoader())) @@ -495,7 +465,7 @@ private Class reloadType(final Class _type) { private ClassModel inspectType(final Class _type) { final Class type = reloadType(_type); - var head = ClassModelHead.create(type, options); + var head = ClassModelHead.create(type); return new ClassModel(head, _Lazy.threadSafe(()->ClassModelBody.create(type, head))); } diff --git a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanFactoryPostProcessor.java b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanFactoryPostProcessor.java index 02caf8457b7..290be48005c 100644 --- a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanFactoryPostProcessor.java +++ b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanFactoryPostProcessor.java @@ -39,10 +39,9 @@ import org.apache.causeway.commons.collections.Can; import org.apache.causeway.commons.internal.base._NullSafe; import org.apache.causeway.commons.internal.base._Timing; -import org.apache.causeway.commons.internal.reflection._ClassCache; import org.apache.causeway.core.config.CausewayModuleCoreConfig; +import org.apache.causeway.core.config.beans.CausewayBeanTypeClassifier.Mode; import org.apache.causeway.core.config.beans.aoppatch.AopPatch; -import org.apache.causeway.core.config.progmodel.ProgrammingModelConstants; import lombok.extern.log4j.Log4j2; @@ -74,7 +73,6 @@ public class CausewayBeanFactoryPostProcessor @Override public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException { this.causewayBeanTypeClassifier = new CausewayBeanTypeClassifier(applicationContext); - installClassCashOptions(); } @Override @@ -105,7 +103,7 @@ public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFac public CausewayBeanTypeClassifier getCausewayBeanTypeClassifier() { return causewayBeanTypeClassifier!=null ? causewayBeanTypeClassifier - : (causewayBeanTypeClassifier = new CausewayBeanTypeClassifier(Can.empty())); // JUnit support + : (causewayBeanTypeClassifier = new CausewayBeanTypeClassifier(Can.empty(), Mode.MOCKUP)); // JUnit support } @Bean(name = CausewayModuleCoreConfig.NAMESPACE + ".CausewayBeanTypeRegistry") @@ -118,13 +116,4 @@ public CausewayBeanTypeRegistry getComponentScanResult() { return new CausewayBeanTypeRegistry(componentScanResult); } - private void installClassCashOptions() { - _ClassCache.Options.builder() - .annotationsVetoingIntrospection( - Can.ofArray(ProgrammingModelConstants.TypeProgrammaticMarker.values()) - .map(ProgrammingModelConstants.TypeProgrammaticMarker::getAnnotationType)) - .build() - .install(); - } - } diff --git a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java index bbb2ddb19c2..d58127ba208 100644 --- a/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java +++ b/core/config/src/main/java/org/apache/causeway/core/config/beans/CausewayBeanMetaData.java @@ -131,31 +131,13 @@ public static CausewayBeanMetaData springNotContributing( final @NonNull LogicalType logicalType) { return new CausewayBeanMetaData(logicalType, BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING, DiscoveredBy.SPRING, ManagedBy.SPRING, PersistenceStack.NONE); } - public static CausewayBeanMetaData unspecified( - final @NonNull LogicalType logicalType, - final @NonNull DiscoveredBy discoveredBy, - final @NonNull BeanSort beanSort) { - return new CausewayBeanMetaData(logicalType, beanSort, discoveredBy, ManagedBy.UNSPECIFIED, PersistenceStack.NONE); - } - - // -- FACTORIES - - @Deprecated - public static CausewayBeanMetaData springManaged( - final @NonNull DiscoveredBy discoveredBy, - final @NonNull BeanSort beanSort, - final @NonNull LogicalType logicalType) { - return new CausewayBeanMetaData(logicalType, beanSort, discoveredBy, ManagedBy.SPRING, PersistenceStack.NONE); - } - /** - * If discovered by Spring, let Spring decide whether it wants to manage this type. We do not interfere. + * If discovered by Spring, let Spring decide whether it wants to manage this type. */ - @Deprecated public static CausewayBeanMetaData unspecified( + final @NonNull LogicalType logicalType, final @NonNull DiscoveredBy discoveredBy, - final @NonNull BeanSort beanSort, - final @NonNull LogicalType logicalType) { + final @NonNull BeanSort beanSort) { return new CausewayBeanMetaData(logicalType, beanSort, discoveredBy, ManagedBy.UNSPECIFIED, PersistenceStack.NONE); } 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 380efbf34fd..bdaba61fcea 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 @@ -46,16 +46,22 @@ @Programmatic public record CausewayBeanTypeClassifier( @NonNull Can activeProfiles, - @NonNull _ClassCache classCache) { + @NonNull _ClassCache classCache, + @NonNull Mode mode) { + + public enum Mode { + SPRING, + MOCKUP + } // -- CONSTRUCTION CausewayBeanTypeClassifier(final ApplicationContext applicationContext) { - this(Can.ofArray(applicationContext.getEnvironment().getActiveProfiles())); + this(Can.ofArray(applicationContext.getEnvironment().getActiveProfiles()), Mode.SPRING); } - CausewayBeanTypeClassifier(final Can activeProfiles) { - this(activeProfiles, _ClassCache.getInstance()); + CausewayBeanTypeClassifier(final Can activeProfiles, final Mode mode) { + this(activeProfiles, _ClassCache.getInstance(), mode); } // -- CLASSIFY @@ -112,14 +118,16 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin // handle introspection veto ... if(TypeProgrammaticMarker.anyMatchOn(typeHead)) { - return CausewayBeanMetaData.springManaged(discoveredBy, BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING, logicalType); - } - // programmatic bean types - if(typeHead.hasIntrospectionVetoingSemantics()) { - return CausewayBeanMetaData.springManaged(discoveredBy, BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING, logicalType); + return switch (mode) { + case SPRING -> switch (discoveredBy) { + case SPRING -> CausewayBeanMetaData.springNotContributing(logicalType); + case CAUSEWAY -> CausewayBeanMetaData.programmatic(named.get()); + }; + case MOCKUP -> CausewayBeanMetaData.springNotContributing(logicalType); + }; } - //[CAUSEWAY-3585] when implements ViewModel, then don't consider alternatives, yield VIEW_MODEL + // when implements ViewModel, yield VIEW_MODEL unless vetoed if(org.apache.causeway.applib.ViewModel.class.isAssignableFrom(type)) { return CausewayBeanMetaData.viewModel(named.get(), discoveredBy); } @@ -131,7 +139,7 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin // domain service if(typeHead.hasAnnotation(DomainService.class)) { - return CausewayBeanMetaData.springManaged(discoveredBy, BeanSort.MANAGED_BEAN_CONTRIBUTING, logicalType); + return CausewayBeanMetaData.springContributing(logicalType); } // entity support @@ -147,7 +155,7 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin if(aDomainObject!=null) { switch (aDomainObject.nature()) { case BEAN: - return CausewayBeanMetaData.unspecified(discoveredBy, BeanSort.MANAGED_BEAN_CONTRIBUTING, named.get()); + return CausewayBeanMetaData.unspecified(named.get(), discoveredBy, BeanSort.MANAGED_BEAN_CONTRIBUTING); case MIXIN: // memoize mixin main name typeHead.attributeMap().put(_ClassCache.Attribute.MIXIN_MAIN_METHOD_NAME, aDomainObject.mixinMethod()); @@ -166,24 +174,26 @@ public CausewayBeanMetaData classify(@NonNull final LogicalType logicalType, fin } if(typeHead.hasAnnotation(Component.class)) { - return CausewayBeanMetaData.unspecified(discoveredBy, BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING, logicalType); + return CausewayBeanMetaData.unspecified(logicalType, discoveredBy, BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING); } // unless explicitly declared otherwise, map records to viewmodels if(type.isRecord()) { - return CausewayBeanMetaData.unspecified(discoveredBy, BeanSort.VIEW_MODEL, named.get()); + return CausewayBeanMetaData.unspecified(named.get(), discoveredBy, BeanSort.VIEW_MODEL); } if(Serializable.class.isAssignableFrom(type)) { - return CausewayBeanMetaData.unspecified(discoveredBy, BeanSort.VALUE, named.get()); + return CausewayBeanMetaData.unspecified(named.get(), discoveredBy, BeanSort.VALUE); } - return CausewayBeanMetaData.unspecified(discoveredBy, BeanSort.UNKNOWN, named.get()); + return CausewayBeanMetaData.unspecified(named.get(), discoveredBy, BeanSort.UNKNOWN); } - //TODO yet this is a naive implementation, not evaluating any expression logic like eg. @Profile("!dev") - //either we find a Spring Boot utility class that does this logic for us, or we make it clear with the - //docs, that we have only limited support for the @Profile annotation + // -- HELPER + + /*TODO yet this is a naive implementation, not evaluating any expression logic like eg. @Profile("!dev") + either we find a Spring Boot utility class that does this logic for us, or we make it clear with the + docs, that we have only limited support for the @Profile annotation*/ private boolean isProfileActive(final String profile) { return activeProfiles.contains(profile); }