diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/IntrospectionTrigger.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/IntrospectionTrigger.java new file mode 100644 index 00000000000..03ff8b9193b --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/IntrospectionTrigger.java @@ -0,0 +1,156 @@ +/* + * 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.core.metamodel.spec; + +import java.util.Optional; +import java.util.function.Function; + +import org.jspecify.annotations.NonNull; + +import org.apache.causeway.applib.id.LogicalType; +import org.apache.causeway.applib.value.semantics.ValueSemanticsProvider; +import org.apache.causeway.core.config.beans.CausewayBeanTypeRegistry; + +/** + * Some times types end up in the meta-model unexpected. + * The {@link IntrospectionTrigger} helps find the cause for why a specific type got introspected. + * + * @since 4.0 + */ +public record IntrospectionTrigger( + IntrospectionCause introspectionCause, + Optional> triggerClass) { + + public enum IntrospectionCause { + /** + * originating directly from {@link CausewayBeanTypeRegistry} + */ + BEAN_CANDIDATE, + /** + * lookup of class directly or via {@link LogicalType} + */ + CLASS_LOOKUP, + /** + * action result (introspection of the action method return) + */ + ACTION_RETURN, + /** + * association (introspection of the getter method return) + */ + GETTER, + + MIXIN, + /** + * class directly reloaded (includes purging of caches) + */ + RELOAD, + /** + * used during reloading, to purge the class-hierarchy from any caches + */ + TEMPORARY, + + TYPE_HIERARCHY, + /** + * originating from {@link CausewayBeanTypeRegistry} categorized as domain service + */ + SERVICE, + /** + * originating from {@link CausewayBeanTypeRegistry} categorized as domain entity + */ + ENTITY, + /** + * originating from {@link CausewayBeanTypeRegistry} categorized as view-model + */ + VIEWMODEL, + /** + * originating from registered {@link ValueSemanticsProvider} + */ + VALUE_TYPE, + + @Deprecated // not used yet - why? + ACTION_PARAM + } + + private final static IntrospectionTrigger BEAN_CANDIDATE = new IntrospectionTrigger( + IntrospectionCause.BEAN_CANDIDATE, Optional.empty()); + private final static IntrospectionTrigger TEMPORARY = new IntrospectionTrigger( + IntrospectionCause.TEMPORARY, Optional.empty()); + + // -- FACTORIES + + public static IntrospectionTrigger beanCandidate() { + return BEAN_CANDIDATE; + } + + public static IntrospectionTrigger typeHierarchy(@NonNull Class cls) { + return new IntrospectionTrigger(IntrospectionCause.TYPE_HIERARCHY, Optional.of(cls)); + } + + public static IntrospectionTrigger valueType(@NonNull Class cls) { + return new IntrospectionTrigger(IntrospectionCause.VALUE_TYPE, Optional.of(cls)); + } + + public static IntrospectionTrigger lookup(@NonNull Class cls) { + return new IntrospectionTrigger(IntrospectionCause.CLASS_LOOKUP, Optional.of(cls)); + } + public static IntrospectionTrigger lookup(@NonNull String logicalTypeNameNotUsed) { + return new IntrospectionTrigger(IntrospectionCause.CLASS_LOOKUP, Optional.empty()); + } + + public static IntrospectionTrigger actionReturn(Class cls) { + return new IntrospectionTrigger(IntrospectionCause.ACTION_RETURN, Optional.of(cls)); + } + + public static IntrospectionTrigger getter(Class cls) { + return new IntrospectionTrigger(IntrospectionCause.GETTER, Optional.of(cls)); + } + + public static IntrospectionTrigger mixin(Class mixinType) { + return new IntrospectionTrigger(IntrospectionCause.MIXIN, Optional.of(mixinType)); + } + + public static IntrospectionTrigger service(Class cls) { + return new IntrospectionTrigger(IntrospectionCause.SERVICE, Optional.of(cls)); + } + + public static IntrospectionTrigger entity(Class cls) { + return new IntrospectionTrigger(IntrospectionCause.SERVICE, Optional.of(cls)); + } + + public static IntrospectionTrigger viewmodel(Class cls) { + return new IntrospectionTrigger(IntrospectionCause.SERVICE, Optional.of(cls)); + } + + public static IntrospectionTrigger temporary(Class domainTypeNotUsed) { + return TEMPORARY; + } + + public static IntrospectionTrigger reload(Class cls) { + return new IntrospectionTrigger(IntrospectionCause.RELOAD, Optional.of(cls)); + } + + /** + * @deprecated refactoring helper + */ + @Deprecated + public static Function, IntrospectionTrigger> dummy() { + return null; + } + +} diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java index 22e37e99899..b6aa801e2ca 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java @@ -116,6 +116,13 @@ class Comparators{ } IntrospectionPolicy getIntrospectionPolicy(); + + /** + * Introduced for debugging. + * @see IntrospectionTrigger + * @since 4.0 + */ + IntrospectionTrigger introspectionTrigger(); /** * Natural order, that is, by {@link BeanSort} then by {@link LogicalType}. diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/FacetedMethodsBuilder.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/FacetedMethodsBuilder.java index c4be6bfda46..7615480a13c 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/FacetedMethodsBuilder.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/FacetedMethodsBuilder.java @@ -54,6 +54,7 @@ import org.apache.causeway.core.metamodel.facets.actcoll.typeof.TypeOfFacet; import org.apache.causeway.core.metamodel.facets.object.mixin.MixinFacet; import org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstitutorRegistry; +import org.apache.causeway.core.metamodel.spec.IntrospectionTrigger; import org.apache.causeway.core.metamodel.spec.impl.ObjectSpecificationMutable.IntrospectionRequest; import org.apache.causeway.core.metamodel.specloader.typeextract.TypeExtractor; @@ -140,17 +141,13 @@ public FacetedMethodsBuilder( this.methodRemover = new ConcurrentMethodRemover(introspectedClass, methodsRemaining); } - // //////////////////////////////////////////////////////////////////////////// - // Class and stuff immediately derived from class - // //////////////////////////////////////////////////////////////////////////// + // -- SHORTCUT private String getClassName() { return introspectedClass.getName(); } - // //////////////////////////////////////////////////////////////////////////// - // introspect class - // //////////////////////////////////////////////////////////////////////////// + // -- INTROSPECT CLASS public void introspectClass() { if (log.isDebugEnabled()) { @@ -163,9 +160,7 @@ public void introspectClass() { .process(introspectedClass, introspectionPolicy(), methodRemover, inspectedTypeSpec); } - // //////////////////////////////////////////////////////////////////////////// - // introspect associations - // //////////////////////////////////////////////////////////////////////////// + // -- INTROSPECT ASSOCIATIONS /** * Returns a {@link List} of {@link FacetedMethod}s representing object @@ -195,7 +190,8 @@ private List createAssociationFacetedMethods() { // Ensure all return types are known TypeExtractor.streamMethodReturn(associationCandidateMethods) .filter(typeToLoad->typeToLoad!=introspectedClass) - .forEach(typeToLoad->specLoader.loadSpecification(typeToLoad, IntrospectionRequest.TYPE_ONLY)); + .forEach(typeToLoad->specLoader.loadSpecification(typeToLoad, IntrospectionRequest.TYPE_ONLY, + IntrospectionTrigger.getter(typeToLoad))); // now create FacetedMethods for collections and for properties var associationFacetedMethods = new ArrayList(); @@ -376,8 +372,9 @@ private boolean representsAction(final ResolvedMethod actionMethod) { // ensure we can load returned element type; otherwise ignore method var anyLoadedAsNull = TypeExtractor.streamMethodReturn(actionMethod) - .map(typeToLoad->specLoaderInternal().loadSpecification(typeToLoad, IntrospectionRequest.TYPE_ONLY)) - .anyMatch(Objects::isNull); + .map(typeToLoad->specLoaderInternal().loadSpecification(typeToLoad, IntrospectionRequest.TYPE_ONLY, + IntrospectionTrigger.actionReturn(typeToLoad))) + .anyMatch(Objects::isNull); if (anyLoadedAsNull) { return false; } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationDefault.java index 6cd28f22706..5cb6a546300 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationDefault.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationDefault.java @@ -104,6 +104,7 @@ import org.apache.causeway.core.metamodel.object.ManagedObjects; import org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstitutorRegistry; import org.apache.causeway.core.metamodel.spec.ActionScope; +import org.apache.causeway.core.metamodel.spec.IntrospectionTrigger; import org.apache.causeway.core.metamodel.spec.ObjectSpecification; import org.apache.causeway.core.metamodel.spec.feature.MixedIn; import org.apache.causeway.core.metamodel.spec.feature.ObjectAction; @@ -115,6 +116,7 @@ import static org.apache.causeway.commons.internal.base._NullSafe.stream; import lombok.Getter; +import lombok.experimental.Accessors; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -133,19 +135,24 @@ final class ObjectSpecificationDefault @Getter(onMethod_={@Override}) private final IntrospectionPolicy introspectionPolicy; + + @Getter(onMethod_={@Override}) @Accessors(fluent = true) + private final IntrospectionTrigger introspectionTrigger; public ObjectSpecificationDefault( final @NonNull CausewayBeanMetaData typeMeta, final @NonNull MetaModelContext mmc, final @NonNull FacetProcessor facetProcessor, final @NonNull PostProcessor postProcessor, - final @NonNull ClassSubstitutorRegistry classSubstitutorRegistry) { + final @NonNull ClassSubstitutorRegistry classSubstitutorRegistry, + final @NonNull IntrospectionTrigger introspectionTrigger) { this.correspondingClass = typeMeta.getCorrespondingClass(); this.logicalType = typeMeta.logicalType(); this.fullName = correspondingClass.getName(); this.shortName = typeMeta.logicalType().logicalSimpleName(); this.beanSort = typeMeta.beanSort(); + this.introspectionTrigger = introspectionTrigger; this.facetHolder = FacetHolder.simple( facetProcessor.getMetaModelContext(), @@ -555,7 +562,7 @@ public final String getFullIdentifier() { } @Override - public void introspect(IntrospectionRequest request) { + public void introspect(IntrospectionRequest request, IntrospectionTrigger introspectionTrigger) { switch (request) { case REGISTER -> introspectUpTo(IntrospectionState.NOT_INTROSPECTED, ()->"introspect(%s)".formatted(request)); @@ -1070,7 +1077,7 @@ private Stream createMixedInAssociations() { private Stream createMixedInAssociation(final Class mixinType) { var mixinSpec = specLoaderInternal().loadSpecification(mixinType, - IntrospectionRequest.FULL); + IntrospectionRequest.FULL, IntrospectionTrigger.mixin(mixinType)); if (mixinSpec == null || mixinSpec == this) { return Stream.empty(); @@ -1104,7 +1111,7 @@ private Stream createMixedInActions() { private Stream createMixedInAction(final Class mixinType) { var mixinSpec = specLoaderInternal().loadSpecification(mixinType, - IntrospectionRequest.FULL); + IntrospectionRequest.FULL, IntrospectionTrigger.mixin(mixinType)); if (mixinSpec == null || mixinSpec == this) { return Stream.empty(); diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationMutable.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationMutable.java index 68f625db7be..7f8ad48053c 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationMutable.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectSpecificationMutable.java @@ -18,6 +18,7 @@ */ package org.apache.causeway.core.metamodel.spec.impl; +import org.apache.causeway.core.metamodel.spec.IntrospectionTrigger; import org.apache.causeway.core.metamodel.spec.ObjectSpecification; interface ObjectSpecificationMutable extends ObjectSpecification { @@ -37,6 +38,6 @@ enum IntrospectionRequest { FULL } - void introspect(IntrospectionRequest request); + void introspect(IntrospectionRequest request, IntrospectionTrigger introspectionTrigger); } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/SpecificationLoaderDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/SpecificationLoaderDefault.java index be0a24ebda4..b22c777afc1 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/SpecificationLoaderDefault.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/SpecificationLoaderDefault.java @@ -78,6 +78,7 @@ import org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstitutor; import org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstitutor.Substitution; import org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstitutorRegistry; +import org.apache.causeway.core.metamodel.spec.IntrospectionTrigger; import org.apache.causeway.core.metamodel.spec.ObjectSpecification; import org.apache.causeway.core.metamodel.spec.feature.ObjectAction; import org.apache.causeway.core.metamodel.spec.impl.ObjectSpecificationMutable.IntrospectionRequest; @@ -265,25 +266,25 @@ public void createMetaModel() { .concat( valueTypesFromProviders.stream(), causewayBeanTypeRegistry.streamScannedTypes()) - // prime (up to NOT_INTROSPECTED) - .map(this::primeSpecification) + // register only (no type-hierarchy nor member introspection) + .map(beanMeta->this.registerSpecification(beanMeta, IntrospectionTrigger.beanCandidate())) .forEach(specs::collect); - introspectAndLog("type hierarchies", specs.knownSpecs, IntrospectionRequest.TYPE_ONLY); - introspectAndLog("value types", specs.valueSpecs.values(), IntrospectionRequest.FULL); - introspectAndLog("mixins", specs.mixinSpecs, IntrospectionRequest.FULL); - introspectAndLog("domain services", specs.domainServiceSpecs, IntrospectionRequest.FULL); + introspectAndLog("type hierarchies", specs.knownSpecs, IntrospectionRequest.TYPE_ONLY, IntrospectionTrigger::typeHierarchy); + introspectAndLog("value types", specs.valueSpecs.values(), IntrospectionRequest.FULL, IntrospectionTrigger::valueType); + introspectAndLog("mixins", specs.mixinSpecs, IntrospectionRequest.FULL, IntrospectionTrigger::mixin); + introspectAndLog("domain services", specs.domainServiceSpecs, IntrospectionRequest.FULL, IntrospectionTrigger::service); introspectAndLog("entities (%s)".formatted(causewayBeanTypeRegistry.persistenceStack().name()), - specs.entitySpecs(), IntrospectionRequest.FULL); - introspectAndLog("view models", specs.viewmodelSpecs(), IntrospectionRequest.FULL); + specs.entitySpecs(), IntrospectionRequest.FULL, IntrospectionTrigger::entity); + introspectAndLog("view models", specs.viewmodelSpecs(), IntrospectionRequest.FULL, IntrospectionTrigger::viewmodel); serviceRegistry.lookupServiceElseFail(MenuBarsService.class).menuBars(); if(isFullIntrospect()) { var snapshot = snapshotSpecifications(); log.info(" - introspecting all {} types eagerly (FullIntrospect=true)", snapshot.size()); - introspect(snapshot.filter(x->x.getBeanSort().isMixin()), IntrospectionRequest.FULL); - introspect(snapshot.filter(x->!x.getBeanSort().isMixin()), IntrospectionRequest.FULL); + introspect(snapshot.filter(x->x.getBeanSort().isMixin()), IntrospectionRequest.FULL, IntrospectionTrigger::mixin); + introspect(snapshot.filter(x->!x.getBeanSort().isMixin()), IntrospectionRequest.FULL, __->IntrospectionTrigger.beanCandidate()); } log.info(" - running remaining validators"); @@ -354,8 +355,18 @@ private boolean isFullIntrospect() { @Override public void reloadSpecification(final Class domainType) { - invalidateCache(domainType); - loadSpecification(domainType, IntrospectionRequest.FULL); + var substitute = classSubstitutorRegistry.getSubstitution(domainType); + if(substitute.isNeverIntrospect()) return; + + var objSpec = + loadSpecification(substitute.apply(domainType), IntrospectionRequest.FULL, IntrospectionTrigger.temporary(domainType)); + + while(objSpec != null) { + cache.remove((Class) objSpec.getCorrespondingClass()); + objSpec = objSpec.superclass(); + } + + loadSpecification(domainType, IntrospectionRequest.FULL, IntrospectionTrigger.reload(domainType)); } @Override @@ -381,8 +392,9 @@ public boolean loadSpecifications(final Class... domainTypes) { @Override public ObjectSpecification loadSpecification( final @Nullable Class type, - final @NonNull IntrospectionRequest request) { - return loadSpecificationNullable(type, this::classify, request); + final @NonNull IntrospectionRequest request, + final @NonNull IntrospectionTrigger introspectionTrigger) { + return loadSpecificationNullable(type, this::classify, request, introspectionTrigger); } @Override @@ -549,10 +561,11 @@ private CausewayBeanMetaData classify(final @Nullable Class type) { } @Nullable - private ObjectSpecificationMutable primeSpecification( - final @NonNull CausewayBeanMetaData typeMeta) { + private ObjectSpecificationMutable registerSpecification( + final @NonNull CausewayBeanMetaData typeMeta, + final @NonNull IntrospectionTrigger introspectionTrigger) { return loadSpecificationNullable( - typeMeta.getCorrespondingClass(), type->typeMeta, IntrospectionRequest.REGISTER); + typeMeta.getCorrespondingClass(), type->typeMeta, IntrospectionRequest.REGISTER, introspectionTrigger); } @@ -560,7 +573,8 @@ private ObjectSpecificationMutable primeSpecification( private ObjectSpecificationMutable loadSpecificationNullable( final @Nullable Class type, final @NonNull Function, CausewayBeanMetaData> beanClassifier, - final @NonNull IntrospectionRequest request) { + final @NonNull IntrospectionRequest request, + final @NonNull IntrospectionTrigger introspectionTrigger) { if(type==null) return null; @@ -572,9 +586,9 @@ private ObjectSpecificationMutable loadSpecificationNullable( var spec = cache.computeIfAbsent(substitutedType, _spec-> logicalTypeResolver .register( - createSpecification(beanClassifier.apply(substitutedType)))); + createSpecification(beanClassifier.apply(substitutedType), introspectionTrigger))); - spec.introspect(request); + spec.introspect(request, introspectionTrigger); if(spec.getAliases().isNotEmpty() // this bool. expr. is an optimization, not strictly required ... a bit of hack though @@ -597,32 +611,35 @@ private ObjectSpecificationMutable loadSpecificationNullable( /** * Creates the appropriate type of {@link ObjectSpecification}. + * @param introspectionTrigger */ - private ObjectSpecificationMutable createSpecification(final CausewayBeanMetaData typeMeta) { + private ObjectSpecificationMutable createSpecification( + final CausewayBeanMetaData typeMeta, + final IntrospectionTrigger introspectionTrigger) { var objectSpec = new ObjectSpecificationDefault( - typeMeta, - metaModelContext, - facetProcessor, - postProcessor, - classSubstitutorRegistry); + typeMeta, metaModelContext, + facetProcessor, postProcessor, + classSubstitutorRegistry, introspectionTrigger); return objectSpec; } private void introspectSequential( final Can specs, - final IntrospectionRequest request) { + final IntrospectionRequest request, + final Function, IntrospectionTrigger> introspectionTriggerFactory) { for (var spec : specs) { - spec.introspect(request); + spec.introspect(request, introspectionTriggerFactory.apply(spec.getCorrespondingClass())); } } private void introspectParallel( final Can specs, - final IntrospectionRequest request) { + final IntrospectionRequest request, + final Function, IntrospectionTrigger> introspectionTriggerFactory) { specs.parallelStream() .forEach(spec -> { try { - spec.introspect(request); + spec.introspect(request, introspectionTriggerFactory.apply(spec.getCorrespondingClass())); } catch (Throwable ex) { log.error("failure", ex); throw ex; @@ -633,34 +650,22 @@ private void introspectParallel( private void introspectAndLog( final String info, final Iterable specs, - final IntrospectionRequest request) { + final IntrospectionRequest request, + final Function, IntrospectionTrigger> introspectionTriggerFactory) { var stopWatch = _Timing.now(); - introspect(Can.ofIterable(specs), request); + introspect(Can.ofIterable(specs), request, introspectionTriggerFactory); stopWatch.stop(); log.info(" - introspecting {} {} took {}ms", _NullSafe.sizeAutodetect(specs), info, stopWatch.getMillis()); } private void introspect( final Can specs, - final IntrospectionRequest request) { + final IntrospectionRequest request, + final Function, IntrospectionTrigger> introspectionTriggerFactory) { if(parallel) { - introspectParallel(specs, request); + introspectParallel(specs, request, introspectionTriggerFactory); } else { - introspectSequential(specs, request); - } - } - - private void invalidateCache(final Class cls) { - var substitute = classSubstitutorRegistry.getSubstitution(cls); - if(substitute.isNeverIntrospect()) return; - - var objSpec = - loadSpecification(substitute.apply(cls), IntrospectionRequest.FULL); - - while(objSpec != null) { - var type = objSpec.getCorrespondingClass(); - cache.remove(type); - objSpec = objSpec.superclass(); + introspectSequential(specs, request, introspectionTriggerFactory); } } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/SpecificationLoaderInternal.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/SpecificationLoaderInternal.java index 47235a2515e..5ccc90b313a 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/SpecificationLoaderInternal.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/SpecificationLoaderInternal.java @@ -32,6 +32,7 @@ import org.apache.causeway.commons.internal.base._Strings; import org.apache.causeway.commons.internal.exceptions._Exceptions; import org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstitutor; +import org.apache.causeway.core.metamodel.spec.IntrospectionTrigger; import org.apache.causeway.core.metamodel.spec.ObjectSpecification; import org.apache.causeway.core.metamodel.spec.impl.ObjectSpecificationMutable.IntrospectionRequest; import org.apache.causeway.core.metamodel.specloader.SpecificationLoader; @@ -47,7 +48,10 @@ interface SpecificationLoaderInternal extends SpecificationLoader { * @return {@code null} if {@code domainType==null}, or if the type should be ignored. */ @Nullable - ObjectSpecification loadSpecification(@Nullable Class domainType, @NonNull IntrospectionRequest request); + ObjectSpecification loadSpecification( + @Nullable Class domainType, + @NonNull IntrospectionRequest request, + @NonNull IntrospectionTrigger introspectionTrigger); // -- SUPPORT FOR LOOKUP BY LOGICAL TYPE NAME @@ -59,14 +63,15 @@ interface SpecificationLoaderInternal extends SpecificationLoader { @Nullable default ObjectSpecification loadSpecification( final @Nullable String logicalTypeName, - final @NonNull IntrospectionRequest request) { + final @NonNull IntrospectionRequest request, + final @NonNull IntrospectionTrigger introspectionTrigger) { if(_Strings.isNullOrEmpty(logicalTypeName)) { return null; } return lookupLogicalType(logicalTypeName) .map(logicalType-> - loadSpecification(logicalType.correspondingClass(), request)) + loadSpecification(logicalType.correspondingClass(), request, introspectionTrigger)) .orElse(null); } @@ -76,7 +81,7 @@ default ObjectSpecification loadSpecification( default Optional specForLogicalTypeName( final @Nullable String logicalTypeName) { return Optional.ofNullable( - loadSpecification(logicalTypeName, IntrospectionRequest.FULL)); + loadSpecification(logicalTypeName, IntrospectionRequest.FULL, IntrospectionTrigger.lookup(logicalTypeName))); } @Override @@ -91,7 +96,7 @@ default Optional specForLogicalType( default Optional specForType( final @Nullable Class domainType) { return Optional.ofNullable( - loadSpecification(domainType, IntrospectionRequest.FULL)); + loadSpecification(domainType, IntrospectionRequest.FULL, IntrospectionTrigger.lookup(domainType))); } @Override @@ -145,13 +150,15 @@ default ObjectSpecification specForBookmarkElseFail( @Override default @Nullable ObjectSpecification loadSpecification( final @Nullable Class domainType) { - return loadSpecification(domainType, IntrospectionRequest.TYPE_ONLY); + return loadSpecification(domainType, IntrospectionRequest.TYPE_ONLY, + IntrospectionTrigger.typeHierarchy(domainType)); } @Override default Optional lookupBeanSort(final @Nullable LogicalType logicalType) { if(logicalType==null) return Optional.empty(); - var spec = loadSpecification(logicalType.correspondingClass(), IntrospectionRequest.REGISTER); + var spec = loadSpecification(logicalType.correspondingClass(), IntrospectionRequest.REGISTER, + IntrospectionTrigger.lookup(logicalType.correspondingClass())); return spec != null ? Optional.of(spec.getBeanSort()) : Optional.empty();