This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch 3900-immutable.config in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 1f37ddb7d7113f16240816607651119d7b879825 Author: Andi Huber <[email protected]> AuthorDate: Fri Aug 1 14:55:53 2025 +0200 CAUSEWAY-3900: lazy spec loader binding - apps back to green --- .../causeway/applib/services/jaxb/JaxbService.java | 52 ++++++------ .../value/semantics/ValueSemanticsAbstract.java | 15 ++-- .../bootstrap/BSMenuBars_roundtrip_Test.java | 2 +- .../applib/services/jaxb/JaxbServiceTest.java | 4 +- .../services/grid/GridSystemServiceAbstract.java | 7 +- .../grid/bootstrap/GridSystemServiceBootstrap.java | 12 +-- .../metamodel/MetaModelServiceDefault.java | 40 ++++----- .../valuesemantics/BigDecimalValueSemantics.java | 6 +- .../valuesemantics/EnumValueSemantics.java | 4 +- .../services/menubars/BSMenuBarsTest.java | 3 +- ...anticsProvider_configureDecimalFormat_Test.java | 6 +- .../mmtestsupport/MetaModelContext_forTesting.java | 5 +- .../bookmarks/BookmarkServiceDefault.java | 22 +++-- .../factory/FactoryServiceDefault.java | 16 ++-- .../runtimeservices/jaxb/JaxbServiceDefault.java | 94 +++++++++++----------- .../session/InteractionServiceDefault.java | 8 +- 16 files changed, 155 insertions(+), 141 deletions(-) diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/jaxb/JaxbService.java b/api/applib/src/main/java/org/apache/causeway/applib/services/jaxb/JaxbService.java index 4ffbbd8373a..366253f22b2 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/services/jaxb/JaxbService.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/services/jaxb/JaxbService.java @@ -19,11 +19,14 @@ package org.apache.causeway.applib.services.jaxb; import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.Marshaller; import jakarta.xml.bind.Unmarshaller; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.apache.causeway.applib.domain.DomainObjectList; @@ -31,7 +34,6 @@ import org.apache.causeway.commons.internal.base._NullSafe; import org.apache.causeway.commons.io.JaxbUtils; -import org.jspecify.annotations.NonNull; import lombok.SneakyThrows; /** @@ -100,7 +102,23 @@ Map<String, String> toXsd( CausewaySchemas causewaySchemas); /** 'Simple' because no injection point resolving or advanced {@link DomainObjectList} handling. */ - class Simple implements JaxbService { + static JaxbService simple() { + return new JaxbServiceInternal(JaxbServiceInternal.Config.simple()); + } + + record JaxbServiceInternal( + Config config + ) implements JaxbService { + + public record Config( + Consumer<Marshaller> marshallerConfigurer, + Consumer<Unmarshaller> unmarshallerConfigurer, + Function<DomainObjectList, JAXBContext> jaxbContextForListProvider) { + /** 'Simple' because no injection point resolving or advanced {@link DomainObjectList} handling. */ + static Config simple() { + return new Config(__->{}, __->{}, __->JaxbUtils.jaxbContextFor(DomainObjectList.class, true)); + } + } @Override @Nullable @@ -109,14 +127,13 @@ public final <T> T fromXml( final @Nullable String xml, final @Nullable Map<String, Object> unmarshallerProperties) { - if (xml == null) { - return null; - } + if (xml == null) return null; + return JaxbUtils.tryRead(domainClass, xml, opts->{ for (var entry : _NullSafe.entrySet(unmarshallerProperties)) { opts.property(entry.getKey(), entry.getValue()); } - opts.unmarshallerConfigurer(this::configure); + opts.unmarshallerConfigurer(config.unmarshallerConfigurer()); return opts; }) .ifFailureFail() @@ -129,14 +146,14 @@ public final String toXml( final @Nullable Map<String, Object> marshallerProperties) { var jaxbContext = domainObject instanceof DomainObjectList - ? jaxbContextForList((DomainObjectList)domainObject) + ? config.jaxbContextForListProvider().apply((DomainObjectList)domainObject) : JaxbUtils.jaxbContextFor(domainObject.getClass(), true); return Try.call(()->JaxbUtils.toStringUtf8(domainObject, opts->{ for (var entry : _NullSafe.entrySet(marshallerProperties)) { opts.property(entry.getKey(), entry.getValue()); } - opts.marshallerConfigurer(this::configure); + opts.marshallerConfigurer(config.marshallerConfigurer()); opts.jaxbContextOverride(jaxbContext); return opts; })) @@ -144,25 +161,6 @@ public final String toXml( .getValue().orElse(null); } - /** - * Optional hook - */ - protected JAXBContext jaxbContextForList(final @NonNull DomainObjectList list) { - return JaxbUtils.jaxbContextFor(DomainObjectList.class, true); - } - - /** - * Optional hook - */ - protected void configure(final Unmarshaller unmarshaller) { - } - - /** - * Optional hook - */ - protected void configure(final Marshaller marshaller) { - } - @Override @SneakyThrows public final Map<String, String> toXsd( diff --git a/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/ValueSemanticsAbstract.java b/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/ValueSemanticsAbstract.java index 54ad4494328..79d584225db 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/ValueSemanticsAbstract.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/ValueSemanticsAbstract.java @@ -33,9 +33,12 @@ import java.util.function.Function; import java.util.function.Supplier; -import org.springframework.beans.factory.annotation.Autowired; +import jakarta.inject.Provider; + +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; +import org.springframework.beans.factory.annotation.Autowired; import org.apache.causeway.applib.annotation.TimePrecision; import org.apache.causeway.applib.exceptions.recoverable.TextEntryParseException; import org.apache.causeway.applib.locale.UserLocale; @@ -56,8 +59,6 @@ import org.apache.causeway.schema.common.v2.ValueType; import org.apache.causeway.schema.common.v2.ValueWithTypeDto; -import org.jspecify.annotations.NonNull; - /** * @since 2.x {@index} */ @@ -387,19 +388,19 @@ protected DateTimeFormatter getTemporalIsoFormat( // -- TRANSLATION SUPPORT @Autowired(required = false) // nullable (JUnit support) - protected TranslationService translationService; + protected Provider<TranslationService> translationService; protected String translate(final String text) { return translationService!=null - ? translationService.translate(TranslationContext.empty(), text) + ? translationService.get().translate(TranslationContext.empty(), text) : text; } // -- PLACEHOLDER RENDERING @Autowired(required = false) // nullable (JUnit support) - private Optional<PlaceholderRenderService> placeholderRenderService = Optional.empty(); + private Optional<Provider<PlaceholderRenderService>> placeholderRenderService = Optional.empty(); protected PlaceholderRenderService getPlaceholderRenderService() { - return placeholderRenderService.orElseGet(PlaceholderRenderService::fallback); + return placeholderRenderService.map(Provider::get).orElseGet(PlaceholderRenderService::fallback); } } diff --git a/api/applib/src/test/java/org/apache/causeway/applib/layout/menubars/bootstrap/BSMenuBars_roundtrip_Test.java b/api/applib/src/test/java/org/apache/causeway/applib/layout/menubars/bootstrap/BSMenuBars_roundtrip_Test.java index 128188d7f1e..fdaa71a1349 100644 --- a/api/applib/src/test/java/org/apache/causeway/applib/layout/menubars/bootstrap/BSMenuBars_roundtrip_Test.java +++ b/api/applib/src/test/java/org/apache/causeway/applib/layout/menubars/bootstrap/BSMenuBars_roundtrip_Test.java @@ -34,7 +34,7 @@ public class BSMenuBars_roundtrip_Test { @BeforeEach public void setUp() throws Exception { - jaxbService = new JaxbService.Simple(); + jaxbService = JaxbService.simple(); } @AfterEach diff --git a/api/applib/src/test/java/org/apache/causeway/applib/services/jaxb/JaxbServiceTest.java b/api/applib/src/test/java/org/apache/causeway/applib/services/jaxb/JaxbServiceTest.java index 93711ff8504..0b954332864 100644 --- a/api/applib/src/test/java/org/apache/causeway/applib/services/jaxb/JaxbServiceTest.java +++ b/api/applib/src/test/java/org/apache/causeway/applib/services/jaxb/JaxbServiceTest.java @@ -35,12 +35,12 @@ class JaxbServiceTest { - private JaxbService.Simple simple; + private JaxbService simple; private ActionInvocationDto sampleDto; @BeforeEach void setUp() throws Exception { - simple = new JaxbService.Simple(); + simple = JaxbService.simple(); sampleDto = getSample(); } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceAbstract.java index 4940ea5751b..a64db222c1a 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceAbstract.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceAbstract.java @@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicInteger; import jakarta.inject.Inject; +import jakarta.inject.Provider; import org.apache.causeway.applib.annotation.Programmatic; import org.apache.causeway.applib.layout.component.ActionLayoutData; @@ -91,7 +92,7 @@ public abstract class GridSystemServiceAbstract<G extends org.apache.causeway.applib.layout.grid.Grid> implements GridSystemService<G> { - protected final SpecificationLoader specificationLoader; + protected final Provider<SpecificationLoader> specLoaderProvider; protected final TranslationService translationService; protected final JaxbService jaxbService; protected final MessageService messageService; @@ -137,7 +138,7 @@ private void overwriteFacets( final G fcGrid, final Class<?> domainClass) { - var objectSpec = specificationLoader.specForTypeElseFail(domainClass); + var objectSpec = specLoaderProvider.get().specForTypeElseFail(domainClass); var oneToOneAssociationById = ObjectMember.mapById(objectSpec.streamProperties(MixedIn.INCLUDED)); var oneToManyAssociationById = ObjectMember.mapById(objectSpec.streamCollections(MixedIn.INCLUDED)); @@ -384,7 +385,7 @@ protected static SurplusAndMissing surplusAndMissing(final Set<String> first, fi @Override public void complete(final G grid, final Class<?> domainClass) { normalize(grid, domainClass); - var objectSpec = specificationLoader.specForTypeElseFail(domainClass); + var objectSpec = specLoaderProvider.get().specForTypeElseFail(domainClass); grid.visit(MetamodelToGridOverridingVisitor.of(objectSpec)); } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java index 33389cbcb0d..dcb5b126526 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java @@ -29,6 +29,7 @@ import jakarta.annotation.Priority; import jakarta.inject.Inject; import jakarta.inject.Named; +import jakarta.inject.Provider; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Lazy; @@ -67,7 +68,6 @@ import org.apache.causeway.core.config.CausewayConfiguration; import org.apache.causeway.core.config.environment.CausewaySystemEnvironment; import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel; -import org.apache.causeway.core.metamodel.context.MetaModelContext; import org.apache.causeway.core.metamodel.facets.actions.position.ActionPositionFacet; import org.apache.causeway.core.metamodel.facets.members.layout.group.GroupIdAndName; import org.apache.causeway.core.metamodel.facets.members.layout.group.LayoutGroupFacet; @@ -79,6 +79,7 @@ import org.apache.causeway.core.metamodel.spec.feature.ObjectAssociation; import org.apache.causeway.core.metamodel.spec.feature.ObjectMember; import org.apache.causeway.core.metamodel.spec.feature.OneToOneAssociation; +import org.apache.causeway.core.metamodel.specloader.SpecificationLoader; import static org.apache.causeway.commons.internal.base._NullSafe.stream; @@ -123,14 +124,15 @@ public static interface FallbackLayoutDataSource { @Inject public GridSystemServiceBootstrap( - final MetaModelContext metaModelContext, + final CausewayConfiguration causewayConfiguration, + final Provider<SpecificationLoader> specLoaderProvider, final TranslationService translationService, final JaxbService jaxbService, final MessageService messageService, final CausewaySystemEnvironment causewaySystemEnvironment, final List<FallbackLayoutDataSource> fallbackLayoutDataSources) { - super(metaModelContext.getSpecificationLoader(), translationService, jaxbService, messageService, causewaySystemEnvironment); - this.config = metaModelContext.getConfiguration(); + super(specLoaderProvider, translationService, jaxbService, messageService, causewaySystemEnvironment); + this.config = causewayConfiguration; this.fallbackLayoutDataSources = Can.ofCollection(fallbackLayoutDataSources); } @@ -240,7 +242,7 @@ protected boolean validateAndNormalize( final Class<?> domainClass) { var bsGrid = (BSGrid) grid; - var objectSpec = specificationLoader.specForTypeElseFail(domainClass); + var objectSpec = specLoaderProvider.get().specForTypeElseFail(domainClass); var oneToOneAssociationById = ObjectMember.mapById(objectSpec.streamProperties(MixedIn.INCLUDED)); var oneToManyAssociationById = ObjectMember.mapById(objectSpec.streamCollections(MixedIn.INCLUDED)); diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/metamodel/MetaModelServiceDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/metamodel/MetaModelServiceDefault.java index a1eff732d36..12da736ef91 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/metamodel/MetaModelServiceDefault.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/metamodel/MetaModelServiceDefault.java @@ -27,11 +27,13 @@ import java.util.stream.Collectors; import jakarta.annotation.Priority; -import jakarta.inject.Inject; 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.annotation.PriorityPrecedence; @@ -63,8 +65,6 @@ import org.apache.causeway.core.metamodel.specloader.SpecificationLoader; import org.apache.causeway.schema.metamodel.v2.MetamodelDto; -import org.jspecify.annotations.NonNull; - /** * Default implementation of {@link MetaModelService}. * @@ -74,21 +74,25 @@ @Named(CausewayModuleCoreMetamodel.NAMESPACE + ".MetaModelServiceDefault") @Priority(PriorityPrecedence.MIDPOINT) @Qualifier("Default") -public class MetaModelServiceDefault implements MetaModelService { +public record MetaModelServiceDefault( + Provider<SpecificationLoader> specificationLoaderProvider, + GridService gridService + ) implements MetaModelService { - @Inject private SpecificationLoader specificationLoader; - @Inject private GridService gridService; + private SpecificationLoader specificationLoader() { + return specificationLoaderProvider.get(); + } @Override public Optional<LogicalType> lookupLogicalTypeByName(final @Nullable String logicalTypeName) { - return specificationLoader.specForLogicalTypeName(logicalTypeName) + return specificationLoader().specForLogicalTypeName(logicalTypeName) .map(ObjectSpecification::logicalType); } @Override public Can<LogicalType> logicalTypeAndAliasesFor(final LogicalType logicalType) { Set<LogicalType> logicalTypes = new TreeSet<>(); - specificationLoader.specForLogicalType(logicalType) + specificationLoader().specForLogicalType(logicalType) .ifPresent(objectSpecification -> { logicalTypes.add(logicalType); objectSpecification.getAliases().stream().forEach(logicalTypes::add); @@ -105,7 +109,7 @@ public Can<LogicalType> logicalTypeAndAliasesFor(final @Nullable String logicalT @Override public Optional<LogicalType> lookupLogicalTypeByClass(final @Nullable Class<?> domainType) { - return specificationLoader.specForType(domainType) + return specificationLoader().specForType(domainType) .map(ObjectSpecification::logicalType); } @@ -113,13 +117,13 @@ public Optional<LogicalType> lookupLogicalTypeByClass(final @Nullable Class<?> d public void rebuild(final Class<?> domainType) { gridService.remove(domainType); - specificationLoader.reloadSpecification(domainType); + specificationLoader().reloadSpecification(domainType); } @Override public DomainModel getDomainModel() { - var specifications = specificationLoader.snapshotSpecifications(); + var specifications = specificationLoader().snapshotSpecifications(); final List<DomainMember> rows = _Lists.newArrayList(); for (final ObjectSpecification spec : specifications) { @@ -172,7 +176,7 @@ public BeanSort sortOf( if(domainType == null) { return null; } - final ObjectSpecification objectSpec = specificationLoader.specForType(domainType).orElse(null); + final ObjectSpecification objectSpec = specificationLoader().specForType(domainType).orElse(null); if(objectSpec == null) { return BeanSort.UNKNOWN; } @@ -200,7 +204,7 @@ public BeanSort sortOf(final Bookmark bookmark, final Mode mode) { final Class<?> domainType; switch (mode) { case RELAXED: - domainType = specificationLoader.specForBookmark(bookmark) + domainType = specificationLoader().specForBookmark(bookmark) .map(ObjectSpecification::getCorrespondingClass) .orElse(null); break; @@ -208,7 +212,7 @@ public BeanSort sortOf(final Bookmark bookmark, final Mode mode) { case STRICT: // fall through to... default: - domainType = specificationLoader.specForBookmark(bookmark) + domainType = specificationLoader().specForBookmark(bookmark) .map(ObjectSpecification::getCorrespondingClass) .orElseThrow(()->_Exceptions .noSuchElement("Cannot resolve logical type name %s to a java class", @@ -228,7 +232,7 @@ public CommandDtoProcessor commandDtoProcessorFor(final String memberIdentifier) return null; } - final ObjectSpecification spec = specificationLoader.specForLogicalTypeName(logicalTypeName).orElse(null); + final ObjectSpecification spec = specificationLoader().specForLogicalTypeName(logicalTypeName).orElse(null); if(spec == null) { return null; } @@ -256,13 +260,13 @@ public MetamodelDto exportMetaModel(final Config config) { if(config.isIncludeShadowedFacets()) { metaModelAnnotators.add(new ShadowedFactetAttributeAnnotator(new ExporterConfig(){})); } - return new MetaModelExporter(specificationLoader, metaModelAnnotators) + return new MetaModelExporter(specificationLoader(), metaModelAnnotators) .exportMetaModel(config); } @Override public ObjectGraph exportObjectGraph(final @NonNull BiPredicate<BeanSort, LogicalType> filter) { - var objectSpecs = specificationLoader + var objectSpecs = specificationLoader() .snapshotSpecifications() .stream() .filter(spec->filter.test(spec.getBeanSort(), spec.logicalType())) diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemantics.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemantics.java index 25930e7ae98..07912c73dd5 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemantics.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemantics.java @@ -40,7 +40,7 @@ import org.apache.causeway.applib.value.semantics.ValueSemanticsProvider; import org.apache.causeway.commons.collections.Can; import org.apache.causeway.core.config.CausewayConfiguration; -import org.apache.causeway.core.metamodel.specloader.SpecificationLoader; +import org.apache.causeway.core.metamodel.context.MetaModelContext; import org.apache.causeway.core.metamodel.util.Facets; import org.apache.causeway.schema.common.v2.ValueType; import org.apache.causeway.schema.common.v2.ValueWithTypeDto; @@ -60,8 +60,6 @@ public class BigDecimalValueSemantics Renderer<BigDecimal>, IdStringifier.EntityAgnostic<BigDecimal> { - @Setter @Inject - private SpecificationLoader specificationLoader; @Setter @Inject private CausewayConfiguration causewayConfiguration; @@ -149,6 +147,8 @@ public int typicalLength() { protected void configureDecimalFormat( final Context context, final DecimalFormat format, final FormatUsageFor usedFor) { + var specificationLoader = MetaModelContext.instanceElseFail().getSpecificationLoader(); + var bigDecimalConfig = causewayConfiguration.valueTypes().bigDecimal(); format.setGroupingUsed( usedFor == PARSING diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/EnumValueSemantics.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/EnumValueSemantics.java index ffcb7e116ba..8b6fade9e58 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/EnumValueSemantics.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/EnumValueSemantics.java @@ -84,7 +84,7 @@ protected EnumValueSemantics( final TranslationService translationService, final Class<T> enumClass) { super(); - this.translationService = translationService; + this.translationService = () -> translationService; this.correspondingClass = enumClass; this.maxLength = maxLengthFor(enumClass); this.enumSpecLazy = _Lazy.threadSafe(()->loadEnumSpec(enumClass)); @@ -194,7 +194,7 @@ private String friendlyName(final Context context, final T objectAsEnum) { .map(MmTitleUtils::titleOf) .orElseGet(()->Enums.getFriendlyNameOf(objectAsEnum.name())); - return Optional.ofNullable(translationService) + return Optional.ofNullable(translationService.get()) .map(ts->ts.translate(TranslationContext.forEnum(objectAsEnum), friendlyNameOfEnum)) .orElse(friendlyNameOfEnum); } diff --git a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/menubars/BSMenuBarsTest.java b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/menubars/BSMenuBarsTest.java index c37a578e07a..0118bcaa93d 100644 --- a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/menubars/BSMenuBarsTest.java +++ b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/menubars/BSMenuBarsTest.java @@ -28,7 +28,6 @@ import org.apache.causeway.applib.layout.menubars.bootstrap.BSMenuBars; import org.apache.causeway.applib.services.jaxb.CausewaySchemas; import org.apache.causeway.applib.services.jaxb.JaxbService; -import org.apache.causeway.applib.services.jaxb.JaxbService.Simple; import org.apache.causeway.commons.internal.resources._Resources; class BSMenuBarsTest { @@ -37,7 +36,7 @@ class BSMenuBarsTest { @BeforeEach void setUp() throws Exception { - jaxbService = new Simple() {}; + jaxbService = JaxbService.simple(); } @AfterEach diff --git a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemanticsProvider_configureDecimalFormat_Test.java b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemanticsProvider_configureDecimalFormat_Test.java index 1419d21ba1c..46e9b50d664 100644 --- a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemanticsProvider_configureDecimalFormat_Test.java +++ b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/valuesemantics/BigDecimalValueSemanticsProvider_configureDecimalFormat_Test.java @@ -37,6 +37,7 @@ import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature; import org.apache.causeway.core.metamodel.specloader.SpecificationLoader; import org.apache.causeway.core.mmtestsupport.ConfigurationTester; +import org.apache.causeway.core.mmtestsupport.MetaModelContext_forTesting; class BigDecimalValueSemanticsProvider_configureDecimalFormat_Test { @@ -60,8 +61,11 @@ void setUpObjects() throws Exception { // expecting Mockito.lenient().when(mockSpecificationLoader.loadFeature(mockIdentifier)).thenReturn(Optional.of(mockObjectFeature)); + MetaModelContext_forTesting.builder() + .specificationLoader(mockSpecificationLoader) + .build(); + valueSemantics = new BigDecimalValueSemantics(); - valueSemantics.setSpecificationLoader(mockSpecificationLoader); } @Test diff --git a/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java b/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java index 01a0403e4c1..9f95c613236 100644 --- a/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java +++ b/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java @@ -445,7 +445,7 @@ public WebAppContextPath getWebAppContextPath() { // -- LAYOUT TESTING SUPPORT @Getter(lazy = true) - private final JaxbService jaxbService = new JaxbService.Simple(); + private final JaxbService jaxbService = JaxbService.simple(); @Getter(lazy = true) private final MenuBarsService menuBarsService = createMenuBarsService(); @@ -481,7 +481,8 @@ private final GridService createGridService() { getGridMarshallerService(), List.of( new GridSystemServiceBootstrap( - this, + getConfiguration(), + ()->getSpecificationLoader(), getTranslationService(), getJaxbService(), getMessageService(), diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/bookmarks/BookmarkServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/bookmarks/BookmarkServiceDefault.java index ae0900e0558..d8a8344f6eb 100644 --- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/bookmarks/BookmarkServiceDefault.java +++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/bookmarks/BookmarkServiceDefault.java @@ -24,8 +24,8 @@ import java.util.stream.Collectors; import jakarta.annotation.Priority; -import jakarta.inject.Inject; import jakarta.inject.Named; +import jakarta.inject.Provider; import org.springframework.beans.factory.annotation.Qualifier; import org.jspecify.annotations.Nullable; @@ -42,7 +42,6 @@ import org.apache.causeway.commons.collections.Can; import org.apache.causeway.commons.internal.base._Strings; import org.apache.causeway.commons.internal.exceptions._Exceptions; -import org.apache.causeway.core.metamodel.context.MetaModelContext; import org.apache.causeway.core.metamodel.object.ManagedObject; import org.apache.causeway.core.metamodel.objectmanager.ObjectManager; import org.apache.causeway.core.metamodel.spec.ObjectSpecification; @@ -58,13 +57,12 @@ @Named(CausewayModuleCoreRuntimeServices.NAMESPACE + ".BookmarkServiceDefault") @Priority(PriorityPrecedence.MIDPOINT) @Qualifier("Default") -public class BookmarkServiceDefault implements BookmarkService { - - @Inject private SpecificationLoader specificationLoader; - @Inject private WrapperFactory wrapperFactory; - @Inject private ObjectManager objectManager; - @Inject private MetaModelContext mmc; - @Inject private MetaModelService metaModelService; +public record BookmarkServiceDefault( + Provider<SpecificationLoader> specificationLoaderProvider, + WrapperFactory wrapperFactory, + ObjectManager objectManager, + MetaModelService metaModelService + ) implements BookmarkService { @Override public Optional<Object> lookup(final @Nullable BookmarkHolder bookmarkHolder) { @@ -96,7 +94,7 @@ public List<Bookmark> bookmarksFor(final Object domainObject) { @Override public Optional<Object> lookup(final @Nullable Bookmark bookmark) { try { - return mmc.getObjectManager().loadObject(bookmark) + return objectManager.loadObject(bookmark) .map(ManagedObject::getPojo); } catch(ObjectNotFoundException ex) { return Optional.empty(); @@ -122,7 +120,7 @@ public Optional<Bookmark> bookmarkFor( || cls==null) { return Optional.empty(); } - return specificationLoader.specForType(cls) + return specificationLoaderProvider.get().specForType(cls) .map(ObjectSpecification::logicalType) .map(logicalType->Bookmark.forLogicalTypeAndIdentifier(logicalType, identifier)); } @@ -135,7 +133,7 @@ public Bookmark bookmarkForElseFail(final @Nullable Object domainObject) { ()->_Exceptions.illegalArgument( "cannot create bookmark for type %s", domainObject!=null - ? specificationLoader.specForType(domainObject.getClass()) + ? specificationLoaderProvider.get().specForType(domainObject.getClass()) .map(spec->spec.toString()) .orElseGet(()->domainObject.getClass().getName()) : "<null>")); diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/factory/FactoryServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/factory/FactoryServiceDefault.java index 076baaefd4e..56373acbc38 100644 --- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/factory/FactoryServiceDefault.java +++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/factory/FactoryServiceDefault.java @@ -21,7 +21,6 @@ import java.util.Optional; import jakarta.annotation.Priority; -import jakarta.inject.Inject; import jakarta.inject.Named; import jakarta.inject.Provider; @@ -56,12 +55,13 @@ @Named(CausewayModuleCoreRuntimeServices.NAMESPACE + ".FactoryServiceDefault") @Priority(PriorityPrecedence.MIDPOINT) @Qualifier("Default") -public class FactoryServiceDefault implements FactoryService { +public record FactoryServiceDefault( + InteractionService interactionService, // dependsOn + Provider<SpecificationLoader> specificationLoaderProvider, + CausewaySystemEnvironment causewaySystemEnvironment, + Provider<ObjectLifecyclePublisher> objectLifecyclePublisherProvider) +implements FactoryService { - @Inject InteractionService interactionService; // dependsOn - @Inject private SpecificationLoader specificationLoader; - @Inject private CausewaySystemEnvironment causewaySystemEnvironment; - @Inject private Provider<ObjectLifecyclePublisher> objectLifecyclePublisherProvider; private ObjectLifecyclePublisher objectLifecyclePublisher() { return objectLifecyclePublisherProvider.get(); } @Override @@ -156,7 +156,7 @@ public <T> T create(final @NonNull Class<T> domainClass) { // -- HELPER private ObjectSpecification loadSpecElseFail(final @NonNull Class<?> type) { - return specificationLoader.specForTypeElseFail(type); + return specificationLoaderProvider().get().specForTypeElseFail(type); } /** handles injection, post-construct and publishing */ @@ -185,7 +185,7 @@ private <T> T createObject( @Override public <T> TreeNode<T> treeNode(T root) { - return TreeNode.root(root, _Casts.uncheckedCast(new ObjectTreeAdapter(specificationLoader))); + return TreeNode.root(root, _Casts.uncheckedCast(new ObjectTreeAdapter(specificationLoaderProvider().get()))); } } diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/jaxb/JaxbServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/jaxb/JaxbServiceDefault.java index 662741b2abe..89d1011fb19 100644 --- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/jaxb/JaxbServiceDefault.java +++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/jaxb/JaxbServiceDefault.java @@ -18,16 +18,16 @@ */ package org.apache.causeway.core.runtimeservices.jaxb; +import java.util.Map; + import jakarta.annotation.Priority; -import jakarta.inject.Inject; import jakarta.inject.Named; -import jakarta.xml.bind.JAXBContext; -import jakarta.xml.bind.Marshaller; -import jakarta.xml.bind.Unmarshaller; +import jakarta.inject.Provider; import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import org.apache.causeway.applib.services.jaxb.JaxbService; +import org.jspecify.annotations.Nullable; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; @@ -36,17 +36,15 @@ import org.apache.causeway.applib.jaxb.PersistentEntitiesAdapter; import org.apache.causeway.applib.jaxb.PersistentEntityAdapter; import org.apache.causeway.applib.services.inject.ServiceInjector; -import org.apache.causeway.applib.services.jaxb.JaxbService.Simple; +import org.apache.causeway.applib.services.jaxb.CausewaySchemas; +import org.apache.causeway.applib.services.jaxb.JaxbService; +import org.apache.causeway.commons.functional.Try; import org.apache.causeway.commons.internal.context._Context; import org.apache.causeway.commons.io.JaxbUtils; import org.apache.causeway.core.metamodel.spec.ObjectSpecification; import org.apache.causeway.core.metamodel.specloader.SpecificationLoader; import org.apache.causeway.core.runtimeservices.CausewayModuleCoreRuntimeServices; -import org.jspecify.annotations.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; - /** * Default implementation of {@link JaxbService}. * @since 2.0 {@index} @@ -55,50 +53,56 @@ @Named(CausewayModuleCoreRuntimeServices.NAMESPACE + ".JaxbServiceDefault") @Priority(PriorityPrecedence.MIDPOINT) @Qualifier("Default") -@RequiredArgsConstructor(onConstructor_ = {@Inject}) -public class JaxbServiceDefault extends Simple { +public record JaxbServiceDefault( + JaxbService delegate) implements JaxbService { - private final ServiceInjector serviceInjector; - private final SpecificationLoader specLoader; + @Autowired + public JaxbServiceDefault( + ServiceInjector serviceInjector, + Provider<SpecificationLoader> specLoaderProvider) { + this(new JaxbService.JaxbServiceInternal(new JaxbService.JaxbServiceInternal.Config( + marshaller->{ + marshaller.setAdapter(PersistentEntityAdapter.class, + serviceInjector.injectServicesInto(new PersistentEntityAdapter())); + marshaller.setAdapter(PersistentEntitiesAdapter.class, + serviceInjector.injectServicesInto(new PersistentEntitiesAdapter())); + }, + unmarshaller->{ + unmarshaller.setAdapter(PersistentEntityAdapter.class, + serviceInjector.injectServicesInto(new PersistentEntityAdapter())); + unmarshaller.setAdapter(PersistentEntitiesAdapter.class, + serviceInjector.injectServicesInto(new PersistentEntitiesAdapter())); + }, + domainObjectList->{ + var elementCls = Try.call(()->_Context.loadClass(domainObjectList.getElementTypeFqcn())) + .getValue() // silently ignore class loading issues + .orElse(null); + var elementType = specLoaderProvider.get() + .specForType(elementCls) + .map(ObjectSpecification::getCorrespondingClass) + .orElse(null); + if (elementType!=null + && elementType.getAnnotation(XmlJavaTypeAdapter.class) == null) { + return JaxbUtils.jaxbContextFor(DomainObjectList.class, elementType); + } else { + return JaxbUtils.jaxbContextFor(DomainObjectList.class, true); + } + }))); + } - @SneakyThrows @Override - protected JAXBContext jaxbContextForList(final @NonNull DomainObjectList domainObjectList) { - var elementType = specLoader - .specForType(_Context.loadClass(domainObjectList.getElementTypeFqcn())) - .map(ObjectSpecification::getCorrespondingClass) - .orElse(null); - if (elementType!=null - && elementType.getAnnotation(XmlJavaTypeAdapter.class) == null) { - return JaxbUtils.jaxbContextFor(DomainObjectList.class, elementType); - } else { - return JaxbUtils.jaxbContextFor(DomainObjectList.class, true); - } + public <T> T fromXml(Class<T> domainClass, String xml, @Nullable Map<String, Object> unmarshallerProperties) { + return delegate.fromXml(domainClass, xml, unmarshallerProperties); } @Override - protected void configure(final Unmarshaller unmarshaller) { - unmarshaller.setAdapter(PersistentEntityAdapter.class, - serviceInjector.injectServicesInto(new PersistentEntityAdapter())); - unmarshaller.setAdapter(PersistentEntitiesAdapter.class, - serviceInjector.injectServicesInto(new PersistentEntitiesAdapter())); + public String toXml(Object domainObject, @Nullable Map<String, Object> marshallerProperties) { + return delegate.toXml(domainObject, marshallerProperties); } @Override - protected void configure(final Marshaller marshaller) { - -//debug -// marshaller.setListener(new Marshaller.Listener() { -// @Override -// public void beforeMarshal(final Object source) { -// System.err.printf("beforeMarshal %s%n", source); -// } -// }); - - marshaller.setAdapter(PersistentEntityAdapter.class, - serviceInjector.injectServicesInto(new PersistentEntityAdapter())); - marshaller.setAdapter(PersistentEntitiesAdapter.class, - serviceInjector.injectServicesInto(new PersistentEntitiesAdapter())); + public Map<String, String> toXsd(Object domainObject, CausewaySchemas causewaySchemas) { + return delegate.toXsd(domainObject, causewaySchemas); } } diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/session/InteractionServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/session/InteractionServiceDefault.java index 6c5b16d695b..5368b73a0c7 100644 --- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/session/InteractionServiceDefault.java +++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/session/InteractionServiceDefault.java @@ -94,7 +94,7 @@ public class InteractionServiceDefault final ThreadLocal<Stack<InteractionLayer>> interactionLayerStack = ThreadLocal.withInitial(Stack::new); final MetamodelEventService runtimeEventService; - final SpecificationLoader specificationLoader; + final Provider<SpecificationLoader> specificationLoaderProvider; final ServiceInjector serviceInjector; final ClockService clockService; @@ -109,7 +109,7 @@ public class InteractionServiceDefault @Inject public InteractionServiceDefault( final MetamodelEventService runtimeEventService, - final SpecificationLoader specificationLoader, + final Provider<SpecificationLoader> specificationLoaderProvider, final ServiceInjector serviceInjector, final TransactionServiceSpring transactionServiceSpring, final ClockService clockService, @@ -117,7 +117,7 @@ public InteractionServiceDefault( final ConfigurableBeanFactory beanFactory, final InteractionIdGenerator interactionIdGenerator) { this.runtimeEventService = runtimeEventService; - this.specificationLoader = specificationLoader; + this.specificationLoaderProvider = specificationLoaderProvider; this.serviceInjector = serviceInjector; this.transactionServiceSpring = transactionServiceSpring; this.clockService = clockService; @@ -136,6 +136,8 @@ public void init(final ContextRefreshedEvent event) { runtimeEventService.fireBeforeMetamodelLoading(); + var specificationLoader = specificationLoaderProvider.get(); + var taskList = _ConcurrentTaskList.named("CausewayInteractionFactoryDefault Init") .addRunnable("SpecificationLoader::createMetaModel", specificationLoader::createMetaModel) .addRunnable("ChangesDtoUtils::init", ChangesDtoUtils::init)
