This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
The following commit(s) were added to refs/heads/v4 by this push:
new fc255d426c8 CAUSEWAY-3892: improved logging of re-validation causes
fc255d426c8 is described below
commit fc255d426c8915161f6150242f646eb8370a6d28
Author: a.huber <[email protected]>
AuthorDate: Mon Aug 25 09:28:59 2025 +0200
CAUSEWAY-3892: improved logging of re-validation causes
---
.../metamodel/spec/impl/FacetedMethodsBuilder.java | 8 +-
.../spec/impl/ObjectSpecificationDefault.java | 155 ++++++++++++---------
.../spec/impl/ObjectSpecificationMutable.java | 36 ++---
.../spec/impl/SpecificationLoaderDefault.java | 84 +++++------
.../spec/impl/SpecificationLoaderInternal.java | 47 +++++--
.../metamodel/specloader/SpecificationLoader.java | 9 +-
.../impl/IntrospectionState_comparable_Test.java | 2 +-
7 files changed, 181 insertions(+), 160 deletions(-)
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 6b59700e33e..c4be6bfda46 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,7 +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.impl.ObjectSpecificationMutable.IntrospectionState;
+import
org.apache.causeway.core.metamodel.spec.impl.ObjectSpecificationMutable.IntrospectionRequest;
import org.apache.causeway.core.metamodel.specloader.typeextract.TypeExtractor;
import lombok.Getter;
@@ -195,7 +195,7 @@ private List<FacetedMethod>
createAssociationFacetedMethods() {
// Ensure all return types are known
TypeExtractor.streamMethodReturn(associationCandidateMethods)
.filter(typeToLoad->typeToLoad!=introspectedClass)
- .forEach(typeToLoad->specLoader.loadSpecification(typeToLoad,
IntrospectionState.TYPE_INTROSPECTED));
+ .forEach(typeToLoad->specLoader.loadSpecification(typeToLoad,
IntrospectionRequest.TYPE_ONLY));
// now create FacetedMethods for collections and for properties
var associationFacetedMethods = new ArrayList<FacetedMethod>();
@@ -376,7 +376,7 @@ 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,
IntrospectionState.TYPE_INTROSPECTED))
+ .map(typeToLoad->specLoaderInternal().loadSpecification(typeToLoad,
IntrospectionRequest.TYPE_ONLY))
.anyMatch(Objects::isNull);
if (anyLoadedAsNull) {
return false;
@@ -431,7 +431,7 @@ private boolean isMixinMain(final ResolvedMethod method) {
.orElse(null);
if(mixinFacet==null) return false;
-
if(inspectedTypeSpec.isLessThan(IntrospectionState.FULLY_INTROSPECTED)) {
+ if(!inspectedTypeSpec.isFullyIntrospected()) {
// members are not introspected yet, so make a guess
return mixinFacet.isCandidateForMain(method);
}
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 c53bb29536d..fef33c364eb 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
@@ -26,6 +26,7 @@
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -55,7 +56,6 @@
import
org.apache.causeway.commons.internal.collections._Multimaps.ListMultimap;
import org.apache.causeway.commons.internal.collections._Sets;
import org.apache.causeway.commons.internal.collections._Streams;
-import org.apache.causeway.commons.internal.exceptions._Exceptions;
import org.apache.causeway.commons.internal.reflection._ClassCache;
import
org.apache.causeway.commons.internal.reflection._GenericResolver.ResolvedMethod;
import
org.apache.causeway.commons.internal.reflection._MethodFacades.MethodFacade;
@@ -114,6 +114,7 @@
import static org.apache.causeway.commons.internal.base._NullSafe.stream;
+import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@@ -315,7 +316,8 @@ public Optional<ObjectAction> getDeclaredAction(
final ImmutableEnumSet<ActionScope> actionScopes,
final MixedIn mixedIn) {
- introspectUpTo(IntrospectionState.FULLY_INTROSPECTED);
+ introspectUpTo(IntrospectionState.FULLY_INTROSPECTED,
+ ()->"getDeclaredAction %s on %s".formatted(id,
this.getFeatureIdentifier()));
return _Strings.isEmpty(id)
? Optional.empty()
@@ -329,7 +331,8 @@ public Optional<ObjectAction> getDeclaredAction(
@Override
public Optional<? extends ObjectMember> getMember(final ResolvedMethod
method) {
- introspectUpTo(IntrospectionState.FULLY_INTROSPECTED);
+ introspectUpTo(IntrospectionState.FULLY_INTROSPECTED,
+ ()->"getMember %s on %s".formatted(method.name(),
this.getFeatureIdentifier()));
if (membersByMethod == null) {
this.membersByMethod = catalogueMembers();
@@ -555,60 +558,85 @@ public final String getFullIdentifier() {
}
@Override
- public void introspectUpTo(final IntrospectionState upTo) {
+ public void introspect(IntrospectionRequest request) {
+ switch (request) {
+ case REGISTER ->
introspectUpTo(IntrospectionState.NOT_INTROSPECTED,
+ ()->"introspect(%s)".formatted(request));
+ case TYPE_ONLY ->
introspectUpTo(IntrospectionState.TYPE_INTROSPECTED,
+ ()->"introspect(%s)".formatted(request));
+ case FULL -> introspectUpTo(IntrospectionState.FULLY_INTROSPECTED,
+ ()->"introspect(%s)".formatted(request));
+ }
+ }
+
+ enum IntrospectionState {
+ /**
+ * At this stage, {@link LogicalType} only.
+ */
+ NOT_INTROSPECTED,
+ /**
+ * Interim stage, to avoid infinite loops while on way to being {@link
#TYPE_INTROSPECTED}
+ */
+ TYPE_BEING_INTROSPECTED,
+ /**
+ * Type has been introspected (but not its members).
+ */
+ TYPE_INTROSPECTED,
+ /**
+ * Interim stage, to avoid infinite loops while on way to being {@link
#FULLY_INTROSPECTED}
+ */
+ MEMBERS_BEING_INTROSPECTED,
+ /**
+ * Fully introspected... class and also its members.
+ */
+ FULLY_INTROSPECTED
+ }
- if(!isLessThan(upTo)) {
- return; // optimization
+ /**
+ * keeps track of the causal chain of introspection requests
+ */
+ @AllArgsConstructor
+ private final static class IntrospectionContext {
+ final String info;
+ final IntrospectionContext cause;
+ @Override
+ public String toString() {
+ return cause!=null
+ ? info + "caused by " + cause.toString()
+ : info;
}
+ }
+
+ private void introspectUpTo(final IntrospectionState upTo,
Supplier<String> introspectionContextProvider) {
+ if(!isLessThan(upTo)) return; // optimization
if(log.isDebugEnabled()) {
log.debug("introspectingUpTo: {}, {}", getFullIdentifier(), upTo);
}
- boolean revalidate = false;
-
switch (introspectionState) {
- case NOT_INTROSPECTED:
- if(isLessThan(upTo)) {
- introspectType();
+ case NOT_INTROSPECTED->{
+ if(isLessThan(upTo)) {
+ introspectType();
+ }
+ if(isLessThan(upTo)) {
+ introspectFully();
+ specLoaderInternal().validateLater(this,
introspectionContextProvider);
+ }
}
- if(isLessThan(upTo)) {
- introspectFully();
- revalidate = true;
+ case TYPE_BEING_INTROSPECTED->{} // nothing to do (interim state
during introspectType)
+ case TYPE_INTROSPECTED->{
+ if(isLessThan(upTo)) {
+ introspectFully();
+ specLoaderInternal().validateLater(this,
introspectionContextProvider);
+ }
}
- // set to avoid infinite loops
- break;
-
- case TYPE_BEING_INTROSPECTED:
- // nothing to do (interim state during introspectType)
- break;
-
- case TYPE_INTROSPECTED:
- if(isLessThan(upTo)) {
- introspectFully();
- revalidate = true;
- }
- break;
-
- case MEMBERS_BEING_INTROSPECTED:
- // nothing to do (interim state during introspectully)
- break;
-
- case FULLY_INTROSPECTED:
- // nothing to do ... all done
- break;
-
- default:
- throw _Exceptions.unexpectedCodeReach();
- }
-
- if(revalidate) {
- getSpecificationLoader().validateLater(this);
+ case MEMBERS_BEING_INTROSPECTED->{}// nothing to do (interim state
during introspect fully)
+ case FULLY_INTROSPECTED->{}// nothing to do ... all done
}
}
private void introspectType() {
-
// set to avoid infinite loops
this.introspectionState = IntrospectionState.TYPE_BEING_INTROSPECTED;
introspectTypeHierarchy();
@@ -627,7 +655,7 @@ private void introspectFully() {
Facets.gridPreload(this, null);
}
- boolean isLessThan(final IntrospectionState upTo) {
+ private boolean isLessThan(final IntrospectionState upTo) {
return this.introspectionState.compareTo(upTo) < 0;
}
@@ -988,7 +1016,8 @@ public boolean hasSubclasses() {
@Override
public Stream<ObjectAssociation> streamDeclaredAssociations(final MixedIn
mixedIn) {
- introspectUpTo(IntrospectionState.FULLY_INTROSPECTED);
+ introspectUpTo(IntrospectionState.FULLY_INTROSPECTED,
+ ()->"streamDeclaredAssociations of
%s".formatted(this.getFeatureIdentifier()));
mixedInAssociationAdder.trigger(this::createMixedInAssociationsAndResort); //
only if not already
@@ -1000,30 +1029,26 @@ public Stream<ObjectAssociation>
streamDeclaredAssociations(final MixedIn mixedI
@Override
public Optional<? extends ObjectMember> getMember(final String memberId) {
- introspectUpTo(IntrospectionState.FULLY_INTROSPECTED);
+ introspectUpTo(IntrospectionState.FULLY_INTROSPECTED,
+ ()->"getMember %s of %s".formatted(memberId,
this.getFeatureIdentifier()));
- if(_Strings.isEmpty(memberId)) {
- return Optional.empty();
- }
+ if(_Strings.isEmpty(memberId)) return Optional.empty();
var objectAction = getAction(memberId);
- if(objectAction.isPresent()) {
- return objectAction;
- }
+ if(objectAction.isPresent()) return objectAction;
+
var association = getAssociation(memberId);
- if(association.isPresent()) {
- return association;
- }
+ if(association.isPresent()) return association;
+
return Optional.empty();
}
@Override
public Optional<ObjectAssociation> getDeclaredAssociation(final String id,
final MixedIn mixedIn) {
- introspectUpTo(IntrospectionState.FULLY_INTROSPECTED);
+ introspectUpTo(IntrospectionState.FULLY_INTROSPECTED,
+ ()->"getDeclaredAssociation %s of %s".formatted(id,
this.getFeatureIdentifier()));
- if(_Strings.isEmpty(id)) {
- return Optional.empty();
- }
+ if(_Strings.isEmpty(id)) return Optional.empty();
return streamDeclaredAssociations(mixedIn)
.filter(objectAssociation->objectAssociation.getId().equals(id))
@@ -1040,7 +1065,8 @@ public Stream<ObjectAction> streamRuntimeActions(final
MixedIn mixedIn) {
public Stream<ObjectAction> streamDeclaredActions(
final ImmutableEnumSet<ActionScope> actionScopes,
final MixedIn mixedIn) {
- introspectUpTo(IntrospectionState.FULLY_INTROSPECTED);
+ introspectUpTo(IntrospectionState.FULLY_INTROSPECTED,
+ ()->"streamDeclaredActions of
%s".formatted(this.getFeatureIdentifier()));
mixedInActionAdder.trigger(this::createMixedInActionsAndResort);
@@ -1062,9 +1088,8 @@ private Stream<ObjectAssociation>
createMixedInAssociations() {
}
private Stream<ObjectAssociation> createMixedInAssociation(final Class<?>
mixinType) {
-
var mixinSpec = specLoaderInternal().loadSpecification(mixinType,
- IntrospectionState.FULLY_INTROSPECTED);
+ IntrospectionRequest.FULL);
if (mixinSpec == null
|| mixinSpec == this) {
return Stream.empty();
@@ -1098,7 +1123,7 @@ private Stream<ObjectActionMixedIn>
createMixedInActions() {
private Stream<ObjectActionMixedIn> createMixedInAction(final Class<?>
mixinType) {
var mixinSpec = specLoaderInternal().loadSpecification(mixinType,
- IntrospectionState.FULLY_INTROSPECTED);
+ IntrospectionRequest.FULL);
if (mixinSpec == null
|| mixinSpec == this) {
return Stream.empty();
@@ -1250,4 +1275,8 @@ private void createMixedInAssociationsAndResort() {
private final Can<EntityTitleSubscriber> titleSubscribers =
getServiceRegistry().select(EntityTitleSubscriber.class);
+ boolean isFullyIntrospected() {
+ return this.introspectionState ==
IntrospectionState.FULLY_INTROSPECTED;
+ }
+
}
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 8f3ec29427c..68f625db7be 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,43 +18,25 @@
*/
package org.apache.causeway.core.metamodel.spec.impl;
-import org.apache.causeway.applib.id.LogicalType;
import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
interface ObjectSpecificationMutable extends ObjectSpecification {
-
- public enum IntrospectionState implements Comparable<IntrospectionState> {
- /**
- * At this stage, {@link LogicalType} only.
- */
- NOT_INTROSPECTED,
+ enum IntrospectionRequest {
/**
- * Interim stage, to avoid infinite loops while on way to being {@link
#TYPE_INTROSPECTED}
+ * No introspection, just register the type, that is, create an
initial yet empty {@link ObjectSpecification}.
*/
- TYPE_BEING_INTROSPECTED,
-
+ REGISTER,
/**
- * Type has been introspected (but not its members).
+ * Partial introspection, that only includes type-hierarchy but not
members.
*/
- TYPE_INTROSPECTED,
-
+ TYPE_ONLY,
/**
- * Interim stage, to avoid infinite loops while on way to being {@link
#FULLY_INTROSPECTED}
+ * Full introspection, that includes type-hierarchy and members.
*/
- MEMBERS_BEING_INTROSPECTED,
+ FULL
+ }
- /**
- * Fully introspected... class and also its members.
- */
- FULLY_INTROSPECTED
+ void introspect(IntrospectionRequest request);
- }
-
- /**
- * Introspecting up to the level required.
- * @since 2.0
- */
- void introspectUpTo(IntrospectionState upTo);
-
}
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 99678aeafa3..be0a24ebda4 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
@@ -31,6 +31,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Supplier;
import java.util.stream.Stream;
import jakarta.annotation.PostConstruct;
@@ -40,8 +41,10 @@
import jakarta.inject.Named;
import jakarta.inject.Provider;
-import org.springframework.beans.factory.annotation.Qualifier;
+import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
+
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.apache.causeway.applib.Identifier;
@@ -77,14 +80,13 @@
import
org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstitutorRegistry;
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.IntrospectionState;
+import
org.apache.causeway.core.metamodel.spec.impl.ObjectSpecificationMutable.IntrospectionRequest;
import
org.apache.causeway.core.metamodel.specloader.validator.ValidationFailure;
import
org.apache.causeway.core.metamodel.specloader.validator.ValidationFailures;
import
org.apache.causeway.core.metamodel.valuetypes.ValueSemanticsResolverDefault;
import
org.apache.causeway.core.security.authorization.manager.ActionSemanticsResolver;
import lombok.Getter;
-import org.jspecify.annotations.NonNull;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@@ -267,21 +269,21 @@ public void createMetaModel() {
.map(this::primeSpecification)
.forEach(specs::collect);
- introspectAndLog("type hierarchies", specs.knownSpecs,
IntrospectionState.TYPE_INTROSPECTED);
- introspectAndLog("value types", specs.valueSpecs.values(),
IntrospectionState.FULLY_INTROSPECTED);
- introspectAndLog("mixins", specs.mixinSpecs,
IntrospectionState.FULLY_INTROSPECTED);
- introspectAndLog("domain services", specs.domainServiceSpecs,
IntrospectionState.FULLY_INTROSPECTED);
+ 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("entities
(%s)".formatted(causewayBeanTypeRegistry.persistenceStack().name()),
- specs.entitySpecs(), IntrospectionState.FULLY_INTROSPECTED);
- introspectAndLog("view models", specs.viewmodelSpecs(),
IntrospectionState.FULLY_INTROSPECTED);
+ specs.entitySpecs(), IntrospectionRequest.FULL);
+ introspectAndLog("view models", specs.viewmodelSpecs(),
IntrospectionRequest.FULL);
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()),
IntrospectionState.FULLY_INTROSPECTED);
- introspect(snapshot.filter(x->!x.getBeanSort().isMixin()),
IntrospectionState.FULLY_INTROSPECTED);
+ introspect(snapshot.filter(x->x.getBeanSort().isMixin()),
IntrospectionRequest.FULL);
+ introspect(snapshot.filter(x->!x.getBeanSort().isMixin()),
IntrospectionRequest.FULL);
}
log.info(" - running remaining validators");
@@ -353,7 +355,7 @@ private boolean isFullIntrospect() {
@Override
public void reloadSpecification(final Class<?> domainType) {
invalidateCache(domainType);
- loadSpecification(domainType, IntrospectionState.FULLY_INTROSPECTED);
+ loadSpecification(domainType, IntrospectionRequest.FULL);
}
@Override
@@ -370,7 +372,6 @@ public boolean loadSpecifications(final Class<?>...
domainTypes) {
/**
* Return the specification for the specified class of object.
- *
* <p>
* It is possible for this method to return <tt>null</tt>, for example if
* any of the configured {@link ClassSubstitutor}s has filtered out the
class.
@@ -380,12 +381,14 @@ public boolean loadSpecifications(final Class<?>...
domainTypes) {
@Override
public ObjectSpecification loadSpecification(
final @Nullable Class<?> type,
- final @NonNull IntrospectionState upTo) {
- return loadSpecificationNullable(type, this::classify, upTo);
+ final @NonNull IntrospectionRequest request) {
+ return loadSpecificationNullable(type, this::classify, request);
}
@Override
- public void validateLater(final ObjectSpecification objectSpec) {
+ public void validateLater(
+ final ObjectSpecification objectSpec,
+ final Supplier<String> introspectionContextProvider) {
if(!isMetamodelFullyIntrospected()) {
// don't trigger validation during bootstrapping
// getValidationResult() is lazily populated later on first
request anyway
@@ -396,7 +399,9 @@ public void validateLater(final ObjectSpecification
objectSpec) {
return;
}
- log.info("re-validation triggered by {}", objectSpec);
+ if(log.isInfoEnabled()) {
+ log.info("re-validation triggered by {}",
introspectionContextProvider.get());
+ }
// validators might discover new specs
// to prevent deadlocks, we queue up validation requests to be
processed later
@@ -547,7 +552,7 @@ private CausewayBeanMetaData classify(final @Nullable
Class<?> type) {
private ObjectSpecificationMutable primeSpecification(
final @NonNull CausewayBeanMetaData typeMeta) {
return loadSpecificationNullable(
- typeMeta.getCorrespondingClass(), type->typeMeta,
IntrospectionState.NOT_INTROSPECTED);
+ typeMeta.getCorrespondingClass(), type->typeMeta,
IntrospectionRequest.REGISTER);
}
@@ -555,11 +560,9 @@ private ObjectSpecificationMutable primeSpecification(
private ObjectSpecificationMutable loadSpecificationNullable(
final @Nullable Class<?> type,
final @NonNull Function<Class<?>, CausewayBeanMetaData>
beanClassifier,
- final @NonNull IntrospectionState upTo) {
+ final @NonNull IntrospectionRequest request) {
- if(type==null) {
- return null;
- }
+ if(type==null) return null;
var substitute = classSubstitutorRegistry.getSubstitution(type);
if (substitute.isNeverIntrospect()) return null; // never inspect
@@ -571,11 +574,11 @@ private ObjectSpecificationMutable
loadSpecificationNullable(
.register(
createSpecification(beanClassifier.apply(substitutedType))));
- spec.introspectUpTo(upTo);
+ spec.introspect(request);
if(spec.getAliases().isNotEmpty()
// this bool. expr. is an optimization, not strictly required ...
a bit of hack though
- && upTo == IntrospectionState.TYPE_INTROSPECTED) {
+ && request == IntrospectionRequest.TYPE_ONLY) {
//XXX[3063] hitting this a couple of times
//(~5 see
org.apache.causeway.testdomain.domainmodel.DomainModelTest_usingGoodDomain.aliasesOnDomainServices_shouldBeHonored())
@@ -607,19 +610,19 @@ private ObjectSpecificationMutable
createSpecification(final CausewayBeanMetaDat
private void introspectSequential(
final Can<ObjectSpecificationMutable> specs,
- final IntrospectionState upTo) {
+ final IntrospectionRequest request) {
for (var spec : specs) {
- spec.introspectUpTo(upTo);
+ spec.introspect(request);
}
}
private void introspectParallel(
final Can<ObjectSpecificationMutable> specs,
- final IntrospectionState upTo) {
+ final IntrospectionRequest request) {
specs.parallelStream()
.forEach(spec -> {
try {
- spec.introspectUpTo(upTo);
+ spec.introspect(request);
} catch (Throwable ex) {
log.error("failure", ex);
throw ex;
@@ -630,37 +633,34 @@ private void introspectParallel(
private void introspectAndLog(
final String info,
final Iterable<ObjectSpecificationMutable> specs,
- final IntrospectionState upTo) {
+ final IntrospectionRequest request) {
var stopWatch = _Timing.now();
- introspect(Can.ofIterable(specs), upTo);
+ introspect(Can.ofIterable(specs), request);
stopWatch.stop();
log.info(" - introspecting {} {} took {}ms",
_NullSafe.sizeAutodetect(specs), info, stopWatch.getMillis());
}
private void introspect(
final Can<ObjectSpecificationMutable> specs,
- final IntrospectionState upTo) {
+ final IntrospectionRequest request) {
if(parallel) {
- introspectParallel(specs, upTo);
+ introspectParallel(specs, request);
} else {
- introspectSequential(specs, upTo);
+ introspectSequential(specs, request);
}
}
private void invalidateCache(final Class<?> cls) {
-
var substitute = classSubstitutorRegistry.getSubstitution(cls);
- if(substitute.isNeverIntrospect()) {
- return;
- }
+ if(substitute.isNeverIntrospect()) return;
- ObjectSpecification spec =
- loadSpecification(substitute.apply(cls),
IntrospectionState.FULLY_INTROSPECTED);
+ var objSpec =
+ loadSpecification(substitute.apply(cls),
IntrospectionRequest.FULL);
- while(spec != null) {
- var type = spec.getCorrespondingClass();
+ while(objSpec != null) {
+ var type = objSpec.getCorrespondingClass();
cache.remove(type);
- spec = spec.superclass();
+ objSpec = objSpec.superclass();
}
}
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 c3ffbe5eea6..47235a2515e 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
@@ -19,9 +19,11 @@
package org.apache.causeway.core.metamodel.spec.impl;
import java.util.Optional;
+import java.util.function.Supplier;
import jakarta.inject.Named;
+import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.apache.causeway.applib.id.LogicalType;
@@ -31,15 +33,13 @@
import org.apache.causeway.commons.internal.exceptions._Exceptions;
import
org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstitutor;
import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
-import
org.apache.causeway.core.metamodel.spec.impl.ObjectSpecificationMutable.IntrospectionState;
+import
org.apache.causeway.core.metamodel.spec.impl.ObjectSpecificationMutable.IntrospectionRequest;
import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
-import org.jspecify.annotations.NonNull;
-
interface SpecificationLoaderInternal extends SpecificationLoader {
+
/**
* Return the specification for the specified class of object.
- *
* <p>
* It is possible for this method to return <tt>null</tt>, for example if
* any of the configured {@link ClassSubstitutor}s has filtered out the
class.
@@ -47,8 +47,8 @@ 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 IntrospectionState upTo);
-
+ ObjectSpecification loadSpecification(@Nullable Class<?> domainType,
@NonNull IntrospectionRequest request);
+
// -- SUPPORT FOR LOOKUP BY LOGICAL TYPE NAME
/**
@@ -59,25 +59,27 @@ interface SpecificationLoaderInternal extends
SpecificationLoader {
@Nullable
default ObjectSpecification loadSpecification(
final @Nullable String logicalTypeName,
- final @NonNull IntrospectionState introspectionState) {
+ final @NonNull IntrospectionRequest request) {
if(_Strings.isNullOrEmpty(logicalTypeName)) {
return null;
}
return lookupLogicalType(logicalTypeName)
.map(logicalType->
- loadSpecification(logicalType.correspondingClass(),
introspectionState))
+ loadSpecification(logicalType.correspondingClass(),
request))
.orElse(null);
}
-
+
// -- SHORTCUTS - 1
+ @Override
default Optional<ObjectSpecification> specForLogicalTypeName(
final @Nullable String logicalTypeName) {
return Optional.ofNullable(
- loadSpecification(logicalTypeName,
IntrospectionState.FULLY_INTROSPECTED));
+ loadSpecification(logicalTypeName, IntrospectionRequest.FULL));
}
+ @Override
default Optional<ObjectSpecification> specForLogicalType(
final @Nullable LogicalType logicalType) {
return Optional.ofNullable(logicalType)
@@ -85,12 +87,14 @@ default Optional<ObjectSpecification> specForLogicalType(
.flatMap(this::specForType);
}
+ @Override
default Optional<ObjectSpecification> specForType(
final @Nullable Class<?> domainType) {
return Optional.ofNullable(
- loadSpecification(domainType,
IntrospectionState.FULLY_INTROSPECTED));
+ loadSpecification(domainType, IntrospectionRequest.FULL));
}
+ @Override
default Optional<ObjectSpecification> specForBookmark(
final @Nullable Bookmark bookmark) {
return Optional.ofNullable(bookmark)
@@ -100,6 +104,7 @@ default Optional<ObjectSpecification> specForBookmark(
// -- SHORTCUTS - 2
+ @Override
default ObjectSpecification specForLogicalTypeNameElseFail(
final @Nullable String logicalTypeName) {
return specForLogicalTypeName(logicalTypeName)
@@ -108,6 +113,7 @@ default ObjectSpecification specForLogicalTypeNameElseFail(
_Strings.nullToEmpty(logicalTypeName)));
}
+ @Override
default ObjectSpecification specForLogicalTypeElseFail(
final @Nullable LogicalType logicalType) {
return specForLogicalType(logicalType)
@@ -116,6 +122,7 @@ default ObjectSpecification specForLogicalTypeElseFail(
logicalType));
}
+ @Override
default ObjectSpecification specForTypeElseFail(
final @Nullable Class<?> domainType) {
return specForType(domainType)
@@ -124,6 +131,7 @@ default ObjectSpecification specForTypeElseFail(
domainType));
}
+ @Override
default ObjectSpecification specForBookmarkElseFail(
final @Nullable Bookmark bookmark) {
return specForBookmark(bookmark)
@@ -134,17 +142,26 @@ default ObjectSpecification specForBookmarkElseFail(
// -- CAUTION! (use only during meta-model initialization)
+ @Override
default @Nullable ObjectSpecification loadSpecification(
final @Nullable Class<?> domainType) {
- return loadSpecification(domainType,
IntrospectionState.TYPE_INTROSPECTED);
+ return loadSpecification(domainType, IntrospectionRequest.TYPE_ONLY);
}
-
+
@Override
default Optional<BeanSort> lookupBeanSort(final @Nullable LogicalType
logicalType) {
if(logicalType==null) return Optional.empty();
- var spec = loadSpecification(logicalType.correspondingClass(),
IntrospectionState.NOT_INTROSPECTED);
- return spec != null
+ var spec = loadSpecification(logicalType.correspondingClass(),
IntrospectionRequest.REGISTER);
+ return spec != null
? Optional.of(spec.getBeanSort())
: Optional.empty();
}
+
+ /**
+ * queue {@code objectSpec} for later validation
+ * @param objectSpec
+ * @param introspectionContextProvider
+ */
+ void validateLater(ObjectSpecification objectSpec, Supplier<String>
introspectionContextProvider);
+
}
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/SpecificationLoader.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/SpecificationLoader.java
index fc653675974..bd48f30d9af 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/SpecificationLoader.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/SpecificationLoader.java
@@ -21,6 +21,7 @@
import java.util.Optional;
import java.util.function.Consumer;
+import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.apache.causeway.applib.Identifier;
@@ -38,8 +39,6 @@
import
org.apache.causeway.core.metamodel.specloader.validator.ValidationFailure;
import
org.apache.causeway.core.metamodel.specloader.validator.ValidationFailures;
-import org.jspecify.annotations.NonNull;
-
/**
* Builds the meta-model, utilizing an instance of {@link ProgrammingModel}
*/
@@ -124,12 +123,6 @@ default LogicalType lookupLogicalTypeElseFail(final
@NonNull String logicalTypeN
);
}
- /**
- * queue {@code objectSpec} for later validation
- * @param objectSpec
- */
- void validateLater(ObjectSpecification objectSpec);
-
// -- LOOKUP API
Optional<ObjectSpecification> specForLogicalTypeName(@Nullable String
logicalTypeName);
diff --git
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/spec/impl/IntrospectionState_comparable_Test.java
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/spec/impl/IntrospectionState_comparable_Test.java
index fb55239586c..2f2237bfa34 100644
---
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/spec/impl/IntrospectionState_comparable_Test.java
+++
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/spec/impl/IntrospectionState_comparable_Test.java
@@ -26,7 +26,7 @@
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
-import
org.apache.causeway.core.metamodel.spec.impl.ObjectSpecificationMutable.IntrospectionState;
+import
org.apache.causeway.core.metamodel.spec.impl.ObjectSpecificationDefault.IntrospectionState;
public class IntrospectionState_comparable_Test {