You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2019/04/04 11:54:08 UTC
[isis] branch 2033-IoC_spring updated: ISIS-2033: makes
'SpecificationLoader' an interface
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch 2033-IoC_spring
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/2033-IoC_spring by this push:
new c9e9db0 ISIS-2033: makes 'SpecificationLoader' an interface
c9e9db0 is described below
commit c9e9db0b876e8184eee90e8f7662bae1923f1a9b
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Apr 4 13:53:58 2019 +0200
ISIS-2033: makes 'SpecificationLoader' an interface
- cleans up the SpecificationLoader implementation a bit
- adds some initial command service infrastructure to 'incubator'
Task-Url: https://issues.apache.org/jira/browse/ISIS-2033
---
...ervice.java => BackgroundExecutionService.java} | 15 +-
...ndService.java => CommandSchedulerService.java} | 14 +-
.../isis/applib/services/command/Command.java | 16 +-
.../applib/services/command/CommandContext.java | 11 +-
.../applib/services/factory/FactoryService.java | 9 +-
.../metamodel/MetaModelServiceDefault.java | 4 +-
.../isis/core/metamodel/spec/ObjectSpecId.java | 63 +-
.../specloader/SpecificationCacheDefault.java | 3 +-
.../metamodel/specloader/SpecificationLoader.java | 647 ++-------------------
.../specloader/SpecificationLoaderDefault.java | 606 +++++++++++++++++++
.../specloader/specimpl/FacetedMethodsBuilder.java | 29 +-
.../specloader/specimpl/IntrospectionState.java | 2 +
.../specimpl/ObjectSpecificationAbstract.java | 48 +-
.../traverser/SpecificationTraverser.java | 4 +-
.../isis/progmodels/dflt/JavaReflectorHelper.java | 3 +-
.../ActionAnnotationFacetFactoryTest_Command.java | 23 +-
...ctionAnnotationFacetFactoryTest_Invocation.java | 8 +-
...ctionAnnotationFacetFactoryTest_Publishing.java | 6 +-
.../SpecificationLoaderTestAbstract.java | 2 +-
.../background/BackgroundServiceDefault.java | 14 +-
.../background/CommandInvocationHandler.java | 6 +-
.../background/ForkingInvocationHandler.java | 6 +-
.../factory/FactoryServiceInternalDefault.java | 6 -
.../apache/isis/core/runtime/memento/Memento.java | 3 +-
.../isis/core/runtime/memento/StandaloneData.java | 4 +-
.../wicket/model/mementos/CollectionMemento.java | 7 +-
.../wicket/model/mementos/PropertyMemento.java | 3 +-
.../viewer/wicket/model/mementos/SpecUtils.java | 10 +-
.../src/main/java/isis/incubator/IsisBoot.java | 1 +
.../java/isis/incubator/IsisIncubatorModule.java | 5 +
.../command/CommandSchedulerServiceInMemory.java | 23 +
.../IncubatingBackgroundExecutionService.java | 92 +++
.../IncubatingCommandDtoServiceInternal.java | 56 ++
.../IncubatingCommandInvocationHandler.java | 33 ++
.../command/IncubatingFactoryService.java | 21 +
.../main/java/springapp/dom/customer/Customer.java | 27 +-
.../springapp/dom/customer/CustomerRepository.java | 2 +
.../src/main/java/springapp/dom/email/Email.java | 49 ++
.../java/springapp/dom/email/EmailRepository.java | 11 +
.../springapp/tests/command/CommandDemoBean.java | 29 +
.../java/springapp/tests/command/CommandTest.java | 30 +
.../java/springapp/tests/command/MessageBox.java | 27 +
42 files changed, 1219 insertions(+), 759 deletions(-)
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundService.java b/core/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundExecutionService.java
similarity index 81%
rename from core/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundService.java
rename to core/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundExecutionService.java
index f53f4b2..2e245e6 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundService.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundExecutionService.java
@@ -16,8 +16,6 @@
*/
package org.apache.isis.applib.services.background;
-import org.apache.isis.applib.annotation.Programmatic;
-
/**
* Submit actions to be invoked in the background.
*
@@ -26,32 +24,33 @@ import org.apache.isis.applib.annotation.Programmatic;
* <pre>
* public void submitInvoices() {
* for(Customer customer: customerRepository.findCustomersToInvoice()) {
- * backgroundService.execute(customer).submitInvoice();
+ * backgroundExecutionService.execute(customer).submitInvoice();
* }
* }
*
* @javax.inject.Inject
- * private BackgroundService backgroundService;
+ * private BackgroundExecutionService backgroundExecutionService;
* </pre>
+ *
+ * @since 2.0.0-M3 (renamed from 'BackgroundService')
+ *
*/
-public interface BackgroundService {
+public interface BackgroundExecutionService {
/**
* Returns a proxy around the object (entity or view model) which is then used to obtain the
* signature of the action to be invoked in the background.
*
* <p>
- * To obtain a proxy for a mixin, use {@link BackgroundService2#executeMixin(Class, Object)}.
+ * To obtain a proxy for a mixin, use {@link BackgroundExecutionService#executeMixin(Class, Object)}.
* </p>
*/
- @Programmatic
<T> T execute(final T object);
/**
* Returns a proxy around the mixin object which is then used to obtain the
* signature of the action to be invoked in the background.
*/
- @Programmatic
<T> T executeMixin(Class<T> mixinClass, Object mixedIn);
}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundCommandService.java b/core/applib/src/main/java/org/apache/isis/applib/services/background/CommandSchedulerService.java
similarity index 69%
rename from core/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundCommandService.java
rename to core/applib/src/main/java/org/apache/isis/applib/services/background/CommandSchedulerService.java
index f91fa5a..5d06e36 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundCommandService.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/background/CommandSchedulerService.java
@@ -20,23 +20,19 @@ import org.apache.isis.applib.services.command.Command;
import org.apache.isis.schema.cmd.v1.CommandDto;
/**
- * Persists a {@link org.apache.isis.schema.cmd.v1.CommandDto command-reified} action such that it can be executed asynchronously,
+ * Persists a {@link org.apache.isis.schema.cmd.v1.CommandDto command-reified}
+ * action such that it can be executed asynchronously,
* for example through a Quartz scheduler.
*
* <p>
- * Separate from {@link BackgroundService} primarily so that the default
+ * Separate from {@link BackgroundExecutionService} primarily so that the default
* implementation, <tt>BackgroundServiceDefault</tt> (in <tt>isis-module-background</tt>) can
* delegate to different implementations of this service.
*
- * <p>
- * There is currently only implementation of this service, <tt>BackgroundCommandServiceJdo</tt> in
- * <tt>o.a.i.module:isis-module-command-jdo</tt>. That implementation has no UI and no side-effects (the programmatic
- * API is through {@link org.apache.isis.applib.services.background.BackgroundService}). It is therefore
- * annotated with {@link org.apache.isis.applib.annotation.DomainService} so that it is automatically registered as
- * a service.
+ * @since 2.0.0-M3 (renamed from 'BackgroundCommandService')
*
*/
-public interface BackgroundCommandService {
+public interface CommandSchedulerService {
public void schedule(
final CommandDto dto,
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/command/Command.java b/core/applib/src/main/java/org/apache/isis/applib/services/command/Command.java
index 717b4f5..064f3e3 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/command/Command.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/command/Command.java
@@ -26,8 +26,8 @@ import org.apache.isis.applib.annotation.Programmatic;
import org.apache.isis.applib.clock.Clock;
import org.apache.isis.applib.events.domain.ActionDomainEvent;
import org.apache.isis.applib.services.HasUniqueId;
-import org.apache.isis.applib.services.background.BackgroundCommandService;
-import org.apache.isis.applib.services.background.BackgroundService;
+import org.apache.isis.applib.services.background.CommandSchedulerService;
+import org.apache.isis.applib.services.background.BackgroundExecutionService;
import org.apache.isis.applib.services.bookmark.Bookmark;
import org.apache.isis.applib.services.bookmark.BookmarkService;
import org.apache.isis.applib.services.iactn.Interaction;
@@ -57,7 +57,7 @@ import org.apache.isis.schema.cmd.v1.CommandDto;
* This is done by {@link #next(String)}. There are three possible sequences that might be generated:
* the sequence of changed domain objects being published by the {@link org.apache.isis.applib.services.publish.PublisherService#publish(Interaction.Execution)}; the
* sequence of wrapped action invocations (each being published), and finally one or more background commands
- * that might be scheduled via the {@link BackgroundService}.
+ * that might be scheduled via the {@link BackgroundExecutionService}.
* </p>
*
*/
@@ -133,7 +133,7 @@ public interface Command extends HasUniqueId {
/**
* The mechanism by which this command is to be executed, either synchronously "in the
* {@link CommandExecuteIn#FOREGROUND foreground}" or is to be executed asynchronously "in the
- * {@link CommandExecuteIn#BACKGROUND background}" through the {@link BackgroundCommandService}.
+ * {@link CommandExecuteIn#BACKGROUND background}" through the {@link CommandSchedulerService}.
*/
CommandExecuteIn getExecuteIn();
@@ -158,7 +158,7 @@ public interface Command extends HasUniqueId {
* The (current) executor of this command.
*
* <p>
- * Note that (even for implementations of {@link BackgroundCommandService} that persist {@link Command}s), this
+ * Note that (even for implementations of {@link CommandSchedulerService} that persist {@link Command}s), this
* property is never (likely to be) persisted, because it is always updated to indicate how the command is
* currently being executed.
*
@@ -212,7 +212,7 @@ public interface Command extends HasUniqueId {
// -- parent (property)
/**
- * For actions created through the {@link BackgroundService} and {@link BackgroundCommandService},
+ * For actions created through the {@link BackgroundExecutionService} and {@link CommandSchedulerService},
* captures the parent action.
*/
Command getParent();
@@ -253,7 +253,7 @@ public interface Command extends HasUniqueId {
// -- persistence (property)
/**
- * Whether this command should ultimately be persisted (if the configured {@link BackgroundCommandService} supports
+ * Whether this command should ultimately be persisted (if the configured {@link CommandSchedulerService} supports
* it) or not.
*
* <p>
@@ -270,7 +270,7 @@ public interface Command extends HasUniqueId {
* on whether {@link #setPersistHint(boolean) a hint has been set} by some other means.
*
* <p>
- * For example, a {@link BackgroundCommandService} implementation that creates persisted background commands ought
+ * For example, a {@link CommandSchedulerService} implementation that creates persisted background commands ought
* associate them (via its {@link Command#getParent() parent}) to an original persisted
* {@link Command}. The hinting mechanism allows the service to suggest that the parent command be persisted so
* that the app can then provide a mechanism to find all child background commands for that original parent command.
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/command/CommandContext.java b/core/applib/src/main/java/org/apache/isis/applib/services/command/CommandContext.java
index 0b26c09..26e3405 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/command/CommandContext.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/command/CommandContext.java
@@ -16,13 +16,15 @@
*/
package org.apache.isis.applib.services.command;
+import java.util.function.Supplier;
+
import javax.enterprise.context.RequestScoped;
/**
- * This service (API and implementation) provides access to context information about any {@link Command}.
+ * Holds the request scoped top level {@link Command}.
*/
@RequestScoped
-public class CommandContext {
+public class CommandContext implements Supplier<Command> {
private Command command;
@@ -37,4 +39,9 @@ public class CommandContext {
this.command = command;
}
+ @Override
+ public Command get() {
+ return getCommand();
+ }
+
}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/factory/FactoryService.java b/core/applib/src/main/java/org/apache/isis/applib/services/factory/FactoryService.java
index 8bf5e3a..07d9cd1 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/factory/FactoryService.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/factory/FactoryService.java
@@ -19,7 +19,6 @@
package org.apache.isis.applib.services.factory;
-import org.apache.isis.applib.annotation.Programmatic;
import org.apache.isis.applib.services.repository.RepositoryService;
public interface FactoryService {
@@ -52,14 +51,12 @@ public interface FactoryService {
* method.
* </p>
*/
- @Programmatic
<T> T instantiate(Class<T> domainClass);
-
- @Programmatic
<T> T mixin(Class<T> mixinClass, Object mixedIn);
- @Programmatic
- <T> T m(Class<T> mixinClass, Object mixedIn);
+ default <T> T m(Class<T> mixinClass, Object mixedIn) {
+ return mixin(mixinClass, mixedIn);
+ }
}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java
index 87ead6a..1b2b0c5 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java
@@ -87,11 +87,11 @@ public class MetaModelServiceDefault implements MetaModelService {
@Override
public void rebuild(final Class<?> domainType) {
- specificationLoader.invalidateCache(domainType);
GridService gridService = _CDI.getSingletonElseFail(GridService.class);
gridService.remove(domainType);
- specificationLoader.loadSpecification(domainType);
+
+ specificationLoader.reloadSpecification(domainType);
}
// //////////////////////////////////////
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecId.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecId.java
index 9a7f3a8..7fa62d4 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecId.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecId.java
@@ -18,13 +18,13 @@
*/
package org.apache.isis.core.metamodel.spec;
-import static org.apache.isis.commons.internal.base._With.requiresNotEmpty;
-
import java.io.Serializable;
-import java.util.Objects;
import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
+import lombok.NonNull;
+import lombok.Value;
+
/**
* Represents an {@link ObjectSpecification}, as determined by
* an {@link ObjectSpecIdFacet}.
@@ -32,44 +32,45 @@ import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFac
* <p>
* Has value semantics.
*/
+@Value(staticConstructor = "of")
public final class ObjectSpecId implements Serializable {
private static final long serialVersionUID = 1L;
- private final String specId;
+ @NonNull private final String specId;
- public static ObjectSpecId of(String specId) {
- requiresNotEmpty(specId, "specId");
- return new ObjectSpecId(specId);
- }
-
- private ObjectSpecId(String specId) {
- this.specId = specId;
- }
+// public static ObjectSpecId of(String specId) {
+// requiresNotEmpty(specId, "specId");
+// return new ObjectSpecId(specId);
+// }
+//
+// private ObjectSpecId(String specId) {
+// this.specId = specId;
+// }
public String asString() {
return specId;
}
- @Override
- public int hashCode() {
- return specId.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final ObjectSpecId other = (ObjectSpecId) obj;
- return Objects.equals(specId, other.specId);
- }
+// @Override
+// public int hashCode() {
+// return specId.hashCode();
+// }
+//
+// @Override
+// public boolean equals(Object obj) {
+// if (this == obj) {
+// return true;
+// }
+// if (obj == null) {
+// return false;
+// }
+// if (getClass() != obj.getClass()) {
+// return false;
+// }
+// final ObjectSpecId other = (ObjectSpecId) obj;
+// return Objects.equals(specId, other.specId);
+// }
@Override
public String toString() {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java
index d7d044e..bf3b6d3 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java
@@ -38,7 +38,8 @@ import org.apache.isis.core.metamodel.spec.ObjectSpecification;
* This allows {@link #allSpecifications()} to return a list of specs.
* Later on, {@link #init()} called which populates #classNameBySpecId.
*
- * Attempting to call {@link #getByObjectType(ObjectSpecId)} before {@link #init() initialisation} will result in an
+ * Attempting to call {@link #getByObjectType(ObjectSpecId)} before
+ * {@link #init() initialisation} will result in an
* {@link IllegalStateException}.
*
*/
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
index 9c5a41d..a12e912 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
@@ -1,4 +1,4 @@
-/**
+/*
* 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.
@@ -16,570 +16,38 @@
*/
package org.apache.isis.core.metamodel.specloader;
-import java.util.Collection;
import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Future;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import javax.enterprise.inject.Vetoed;
+import javax.annotation.Nullable;
-import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.annotation.NatureOfService;
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.services.inject.ServiceInjector;
-import org.apache.isis.applib.services.registry.ServiceRegistry;
-import org.apache.isis.applib.services.registry.ServiceRegistry.BeanAdapter;
-import org.apache.isis.commons.internal.base._NullSafe;
-import org.apache.isis.commons.internal.collections._Lists;
-import org.apache.isis.commons.internal.context._Context;
-import org.apache.isis.commons.internal.debug._Probe;
-import org.apache.isis.config.IsisConfiguration;
-import org.apache.isis.config.internal._Config;
import org.apache.isis.config.property.ConfigPropertyBoolean;
import org.apache.isis.config.property.ConfigPropertyEnum;
-import org.apache.isis.config.registry.IsisBeanTypeRegistry;
-import org.apache.isis.core.commons.ensure.Assert;
-import org.apache.isis.core.commons.exceptions.IsisException;
-import org.apache.isis.core.commons.lang.ClassUtil;
-import org.apache.isis.core.metamodel.MetaModelContext;
-import org.apache.isis.core.metamodel.facetapi.Facet;
-import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
-import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
-import org.apache.isis.core.metamodel.spec.FreeStandingList;
import org.apache.isis.core.metamodel.spec.ObjectSpecId;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutor;
-import org.apache.isis.core.metamodel.specloader.facetprocessor.FacetProcessor;
-import org.apache.isis.core.metamodel.specloader.postprocessor.PostProcessor;
-import org.apache.isis.core.metamodel.specloader.specimpl.FacetedMethodsBuilderContext;
import org.apache.isis.core.metamodel.specloader.specimpl.IntrospectionState;
-import org.apache.isis.core.metamodel.specloader.specimpl.ObjectSpecificationAbstract;
-import org.apache.isis.core.metamodel.specloader.specimpl.dflt.ObjectSpecificationDefault;
-import org.apache.isis.core.metamodel.specloader.specimpl.standalonelist.ObjectSpecificationOnStandaloneList;
-import org.apache.isis.core.metamodel.specloader.validator.MetaModelDeficiencies;
-import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidator;
-import org.apache.isis.core.metamodel.specloader.validator.ValidationFailures;
-import org.apache.isis.core.runtime.threadpool.ThreadPoolExecutionMode;
-import org.apache.isis.core.runtime.threadpool.ThreadPoolSupport;
-import org.apache.isis.progmodels.dflt.ProgrammingModelFacetsJava5;
-import org.apache.isis.schema.utils.CommonDtoUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import lombok.val;
/**
* Builds the meta-model.
- *
- * <p>
- * The implementation provides for a degree of pluggability:
- * <ul>
- * <li>The most important plug-in point is {@link ProgrammingModel} that
- * specifies the set of {@link Facet} that make up programming model. If not
- * specified then defaults to {@link ProgrammingModelFacetsJava5} (which should
- * be used as a starting point for your own customizations).
- * <li>The only mandatory plug-in point is {@link ClassSubstitutor}, which
- * allows the class to be loaded to be substituted if required. This is used in
- * conjunction with some <tt>PersistenceMechanism</tt>s that do class
- * enhancement.
- * </ul>
- * </p>
- *
- * <p>
- * Implementing class is added to {@link ServiceInjector} as an (internal) domain service; all public methods
- * must be annotated using {@link Programmatic}.
- * </p>
+ * TODO [2033] add missing java-doc
*
*/
-@Vetoed // has a producer
-public class SpecificationLoader {
-
- private final static Logger LOG = LoggerFactory.getLogger(SpecificationLoader.class);
+public interface SpecificationLoader {
- // -- constructor, fields
- public static final ConfigPropertyBoolean CONFIG_PROPERTY_PARALLELIZE =
+ static final ConfigPropertyBoolean CONFIG_PROPERTY_PARALLELIZE =
new ConfigPropertyBoolean("isis.reflector.introspector.parallelize", true);
- public static final ConfigPropertyEnum<IntrospectionMode> CONFIG_PROPERTY_MODE =
+ static final ConfigPropertyEnum<IntrospectionMode> CONFIG_PROPERTY_MODE =
new ConfigPropertyEnum<>("isis.reflector.introspector.mode", IntrospectionMode.LAZY_UNLESS_PRODUCTION);
-
- private final ClassSubstitutor classSubstitutor = new ClassSubstitutor();
-
- private final ProgrammingModel programmingModel;
- private final FacetProcessor facetProcessor;
-
-
- private final MetaModelValidator metaModelValidator;
- private final SpecificationCacheDefault cache = new SpecificationCacheDefault();
- private final PostProcessor postProcessor;
-
-
- public SpecificationLoader(
- final ProgrammingModel programmingModel,
- final MetaModelValidator metaModelValidator) {
-
- this.programmingModel = programmingModel;
- this.metaModelValidator = metaModelValidator;
-
- this.facetProcessor = new FacetProcessor(programmingModel);
- this.postProcessor = new PostProcessor(programmingModel);
- }
-
-
- // -- init
-
-
- /**
- * Initializes and wires up, and primes the cache based on any service
- * classes (provided by the {@link ServicesInjector}).
- */
- public void init() {
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("initialising {}", this);
- }
-
- // wire subcomponents into each other
- //facetProcessor.setServicesInjector(servicesInjector);
-
- // initialize subcomponents
- this.programmingModel.init();
- facetProcessor.init();
-
- postProcessor.init();
- metaModelValidator.init();
-
-
- // need to completely load services and mixins (synchronously)
- LOG.info("Loading all specs (up to state of {})", IntrospectionState.NOT_INTROSPECTED);
-
- val typeRegistry = IsisBeanTypeRegistry.current();
-
- final List<ObjectSpecification> specificationsFromRegistry = _Lists.newArrayList();
-
- // we use allServiceClasses() - obtained from servicesInjector - rather than reading from the
- // AppManifest.Registry.instance().getDomainServiceTypes(), because the former also has the fallback
- // services set up in IsisSessionFactoryBuilder beforehand.
- final List<ObjectSpecification> domainServiceSpecs =
- loadSpecificationsForBeans(
- streamBeans(), NatureOfService.DOMAIN,
- specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
- );
- final List<ObjectSpecification> mixinSpecs =
- loadSpecificationsFor(
- typeRegistry.getMixinTypes().stream(), null,
- specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
- );
- loadSpecificationsFor(
- CommonDtoUtils.VALUE_TYPES.stream(), null,
- specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
- );
- loadSpecificationsFor(
- typeRegistry.getDomainObjectTypes().stream(), null,
- specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
- );
- loadSpecificationsFor(
- typeRegistry.getViewModelTypes().stream(), null,
- specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
- );
- loadSpecificationsFor(
- typeRegistry.getXmlElementTypes().stream(), null,
- specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
- );
-
- cache.init();
-
- final Collection<ObjectSpecification> cachedSpecifications = allCachedSpecifications();
-
- logBefore(specificationsFromRegistry, cachedSpecifications);
-
- LOG.info("Introspecting all specs up to {}", IntrospectionState.TYPE_INTROSPECTED);
- introspect(specificationsFromRegistry, IntrospectionState.TYPE_INTROSPECTED);
-
- LOG.info("Introspecting domainService specs up to {}", IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
- introspect(domainServiceSpecs, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
-
- LOG.info("Introspecting mixin specs up to {}", IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
- introspect(mixinSpecs, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
-
- logAfter(cachedSpecifications);
-
- final IntrospectionMode mode = CONFIG_PROPERTY_MODE.from(getConfiguration());
- if(mode.isFullIntrospect(_Context.getEnvironment().getDeploymentType())) {
- LOG.info("Introspecting all cached specs up to {}", IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
- introspect(cachedSpecifications, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
- }
-
- LOG.info("init() - done");
-
- //FIXME [2033] remove debug code ...
- //{
-// streamServiceClasses()
-// .forEach(service->probe.println("using service %s", service));
-//
-//
-// val metaModelService = _CDI.getSingleton(MetaModelService.class);
-// val jaxbService = _CDI.getSingleton(JaxbService.class);
-//
-// val metamodelDto =
-// metaModelService.exportMetaModel(
-// new MetaModelService.Config()
-// .withIgnoreNoop()
-// .withIgnoreAbstractClasses()
-// .withIgnoreBuiltInValueTypes()
-// .withIgnoreInterfaces()
-// .withPackagePrefix("domainapp")
-// );
-//
-// final String xml = jaxbService.toXml(metamodelDto);
-// //probe.println(xml);
-// }
- }
+ // -- LIVE CYCLE
- private final static _Probe probe = _Probe.unlimited().label("SpecificationLoader");
-
- private void logBefore(
- final List<ObjectSpecification> specificationsFromRegistry,
- final Collection<ObjectSpecification> cachedSpecifications) {
- if(!LOG.isDebugEnabled()) {
- return;
- }
- LOG.debug(String.format(
- "specificationsFromRegistry.size = %d ; cachedSpecifications.size = %d",
- specificationsFromRegistry.size(), cachedSpecifications.size()));
-
- List<ObjectSpecification> registryNotCached = specificationsFromRegistry.stream()
- .filter(spec -> !cachedSpecifications.contains(spec))
- .collect(Collectors.toList());
- List<ObjectSpecification> cachedNotRegistry = cachedSpecifications.stream()
- .filter(spec -> !specificationsFromRegistry.contains(spec))
- .collect(Collectors.toList());
-
- LOG.debug(String.format(
- "registryNotCached.size = %d ; cachedNotRegistry.size = %d",
- registryNotCached.size(), cachedNotRegistry.size()));
- }
-
- private void logAfter(final Collection<ObjectSpecification> cachedSpecifications) {
- if(!LOG.isDebugEnabled()) {
- return;
- }
-
- final Collection<ObjectSpecification> cachedSpecificationsAfter = cache.allSpecifications();
- List<ObjectSpecification> cachedAfterNotBefore = cachedSpecificationsAfter.stream()
- .filter(spec -> !cachedSpecifications.contains(spec))
- .collect(Collectors.toList());
- LOG.debug(String.format("cachedSpecificationsAfter.size = %d ; cachedAfterNotBefore.size = %d",
- cachedSpecificationsAfter.size(), cachedAfterNotBefore.size()));
- }
-
- private void introspect(final Collection<ObjectSpecification> specs, final IntrospectionState upTo) {
- final List<Callable<Object>> callables = _Lists.newArrayList();
- for (final ObjectSpecification specification : specs) {
- Callable<Object> callable = new Callable<Object>() {
- @Override
- public Object call() {
-
- final ObjectSpecificationAbstract specSpi = (ObjectSpecificationAbstract) specification;
- specSpi.introspectUpTo(upTo);
-
- return null;
- }
- public String toString() {
- return String.format(
- "%s: #introspectUpTo( %s )",
- specification.getFullIdentifier(), upTo);
- }
- };
- callables.add(callable);
- }
-
- invokeAndWait(callables);
- }
-
- private void invokeAndWait(final List<Callable<Object>> callables) {
- final ThreadPoolSupport threadPoolSupport = ThreadPoolSupport.getInstance();
- final boolean parallelize = CONFIG_PROPERTY_PARALLELIZE.from(getConfiguration());
-
- final ThreadPoolExecutionMode executionModeFromConfig = parallelize
- ? ThreadPoolExecutionMode.PARALLEL
- : ThreadPoolExecutionMode.SEQUENTIAL;
-
- final List<Future<Object>> futures =
- threadPoolSupport.invokeAll(executionModeFromConfig, callables);
- threadPoolSupport.joinGatherFailures(futures);
- }
-
- private List<ObjectSpecification> loadSpecificationsFor(
- final Stream<Class<?>> domainTypes,
- final NatureOfService natureOfServiceFallback,
- final List<ObjectSpecification> appendTo,
- final IntrospectionState upTo) {
-
- return domainTypes
- .map(domainType->internalLoadSpecification(domainType, natureOfServiceFallback, upTo))
- .filter(_NullSafe::isPresent)
- .peek(appendTo::add)
- .collect(Collectors.toList());
- }
-
- private List<ObjectSpecification> loadSpecificationsForBeans (
- final Stream<BeanAdapter> beans,
- final NatureOfService natureOfServiceFallback,
- final List<ObjectSpecification> appendTo,
- final IntrospectionState upTo) {
-
- return beans
- .filter(bean->bean.isDomainService())
- .map(bean->bean.getBean().getBeanClass())
- .map(domainType->internalLoadSpecification(domainType, natureOfServiceFallback, upTo))
- .filter(_NullSafe::isPresent)
- .peek(appendTo::add)
- .collect(Collectors.toList());
- }
-
- // -- shutdown
-
- public void shutdown() {
- LOG.info("shutting down {}", this);
-
- cache.clear();
- }
-
- // -- invalidateCache
-
- public void invalidateCache(final Class<?> cls) {
-
- if(!cache.isInitialized()) {
- // could be called by JRebel plugin, before we are up-and-running
- // just ignore.
- return;
- }
- final Class<?> substitutedType = classSubstitutor.getClass(cls);
-
- if(substitutedType.isAnonymousClass()) {
- // JRebel plugin might call us... just ignore 'em.
- return;
- }
-
- ObjectSpecification spec = loadSpecification(substitutedType, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
- while(spec != null) {
- final Class<?> type = spec.getCorrespondingClass();
- cache.remove(type.getName());
- if(spec.containsDoOpFacet(ObjectSpecIdFacet.class)) {
- // umm. Some specs do not have an ObjectSpecIdFacet...
- recache(spec);
- }
- spec = spec.superclass();
- }
- }
-
-
- private void recache(final ObjectSpecification newSpec) {
- cache.recache(newSpec);
- }
-
- // -- validation
-
- private ValidationFailures validationFailures;
-
- public MetaModelDeficiencies validateThenGetDeficienciesIfAny() {
- final IntrospectionMode mode = CONFIG_PROPERTY_MODE.from(getConfiguration());
- if(!mode.isFullIntrospect(_Context.getEnvironment().getDeploymentType())) {
- LOG.info("Meta model validation skipped (full introspection of metamodel not configured)");
- return null;
- }
-
- ValidationFailures validationFailures = validate();
- return validationFailures.getDeficienciesIfAny();
- }
-
- public ValidationFailures validate() {
- if(validationFailures == null) {
- validationFailures = new ValidationFailures();
- metaModelValidator.validate(validationFailures);
- }
- return validationFailures;
- }
-
- // -- loadSpecification, loadSpecifications
-
- /**
- * Return the specification for the specified class of object.
- *
- * <p>
- * It is possible for this method to return <tt>null</tt>, for example if
- * the configured {@link org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutor}
- * has filtered out the class.
- */
- public ObjectSpecification loadSpecification(final String className) {
- return loadSpecification(className, IntrospectionState.TYPE_INTROSPECTED);
- }
-
- public ObjectSpecification loadSpecification(final String className, final IntrospectionState upTo) {
- assert className != null;
-
- try {
- final Class<?> cls = loadBuiltIn(className);
- return internalLoadSpecification(cls, null, upTo);
- } catch (final ClassNotFoundException e) {
- final ObjectSpecification spec = cache.get(className);
- if (spec == null) {
- throw new IsisException("No such class available: " + className);
- }
- return spec;
- }
- }
-
- public ObjectSpecification loadSpecification(final Class<?> type) {
- return loadSpecification(type, IntrospectionState.TYPE_INTROSPECTED);
- }
-
- @Programmatic
- public ObjectSpecification peekSpecification(final Class<?> type) {
-
- final Class<?> substitutedType = classSubstitutor.getClass(type);
- if (substitutedType == null) {
- return null;
- }
-
- final String typeName = substitutedType.getName();
- ObjectSpecification spec = cache.get(typeName);
- if (spec != null) {
- return spec;
- }
-
- return null;
- }
-
- public ObjectSpecification loadSpecification(final Class<?> type, final IntrospectionState upTo) {
- final ObjectSpecification spec = internalLoadSpecification(type, null, upTo);
- if(spec == null) {
- return null;
- }
-
- // TODO: review, is this now needed?
- // We now create the ObjectSpecIdFacet immediately after creating the ObjectSpecification,
- // so the cache shouldn't need updating here also.
- if(cache.isInitialized()) {
- // umm. It turns out that anonymous inner classes (eg org.estatio.dom.WithTitleGetter$ToString$1)
- // don't have an ObjectSpecId; hence the guard.
- if(spec.containsDoOpFacet(ObjectSpecIdFacet.class)) {
- ObjectSpecId specId = spec.getSpecId();
- if (cache.getByObjectType(specId) == null) {
- cache.recache(spec);
- }
- }
- }
- return spec;
- }
-
- private ObjectSpecification internalLoadSpecification(
- final Class<?> type,
- final NatureOfService natureFallback,
- final IntrospectionState upTo) {
-
- final Class<?> substitutedType = classSubstitutor.getClass(type);
- if (substitutedType == null) {
- return null;
- }
- Assert.assertNotNull(substitutedType);
-
- final String typeName = substitutedType.getName();
- ObjectSpecification spec = cache.get(typeName);
- if (spec != null) {
- return spec;
- }
-
- synchronized (this) {
- // inside the synchronized block
- spec = cache.get(typeName);
- if (spec != null) {
- return spec;
- }
-
- final ObjectSpecification specification = createSpecification(substitutedType, natureFallback);
-
- // put into the cache prior to introspecting, to prevent
- // infinite loops
- cache.cache(typeName, specification);
-
- final ObjectSpecificationAbstract specSpi = (ObjectSpecificationAbstract) specification;
- specSpi.introspectUpTo(upTo);
-
- return specification;
- }
- }
-
- /**
- * Loads the specifications of the specified types except the one specified
- * (to prevent an infinite loop).
- */
- public boolean loadSpecifications(
- final List<Class<?>> typesToLoad,
- final Class<?> typeToIgnore,
- final IntrospectionState upTo) {
- boolean anyLoadedAsNull = false;
- for (final Class<?> typeToLoad : typesToLoad) {
- if (typeToLoad != typeToIgnore) {
- final ObjectSpecification objectSpecification =
- internalLoadSpecification(typeToLoad, null, upTo);
- final boolean loadedAsNull = (objectSpecification == null);
- anyLoadedAsNull = loadedAsNull || anyLoadedAsNull;
- }
- }
- return anyLoadedAsNull;
- }
-
- /**
- * Creates the appropriate type of {@link ObjectSpecification}.
- */
- private ObjectSpecification createSpecification(
- final Class<?> cls,
- final NatureOfService fallback) {
-
- // ... and create the specs
- final ObjectSpecificationAbstract objectSpec;
- if (FreeStandingList.class.isAssignableFrom(cls)) {
-
- objectSpec = new ObjectSpecificationOnStandaloneList(facetProcessor, postProcessor);
-
- } else {
-
- final FacetedMethodsBuilderContext facetedMethodsBuilderContext =
- new FacetedMethodsBuilderContext(
- this, facetProcessor);
-
- final NatureOfService natureOfServiceIfAny = natureOfServiceFrom(cls, fallback);
-
- objectSpec = new ObjectSpecificationDefault(cls,
- facetedMethodsBuilderContext,
- facetProcessor, natureOfServiceIfAny, postProcessor);
- }
-
- return objectSpec;
- }
-
- private NatureOfService natureOfServiceFrom(
- final Class<?> type,
- final NatureOfService fallback) {
- final DomainService domainServiceIfAny = type.getAnnotation(DomainService.class);
- return domainServiceIfAny != null ? domainServiceIfAny.nature() : fallback;
- }
-
- private Class<?> loadBuiltIn(final String className) throws ClassNotFoundException {
- final Class<?> builtIn = ClassUtil.getBuiltIn(className);
- if (builtIn != null) {
- return builtIn;
- }
- return ClassUtil.forName(className);
- }
-
- // -- allSpecifications
- /**
+ void init();
+
+ void shutdown();
+
+ // -- LOOKUP
+
+ /**
* Returns (a new list holding a copy of) all the loaded specifications.
*
* <p>
@@ -588,49 +56,48 @@ public class SpecificationLoader {
* ObjectSpec's being discovered, eg performing metamodel validation.
* </p>
*/
- public List<ObjectSpecification> allSpecifications() {
- return _Lists.newArrayList(allCachedSpecifications());
- }
-
- private Collection<ObjectSpecification> allCachedSpecifications() {
- return cache.allSpecifications();
- }
-
- private Stream<BeanAdapter> streamBeans() {
- final ServiceRegistry registry = MetaModelContext.current().getServiceRegistry();
- return registry.streamRegisteredBeans();
- }
-
- // -- loaded
- /**
- * Whether this class has been loaded.
- */
- public boolean loaded(final Class<?> cls) {
- return loaded(cls.getName());
- }
-
- /**
- * @see #loaded(Class).
- */
- public boolean loaded(final String fullyQualifiedClassName) {
- return cache.get(fullyQualifiedClassName) != null;
- }
-
- // -- lookupBySpecId
- public ObjectSpecification lookupBySpecId(ObjectSpecId objectSpecId) {
- if(!cache.isInitialized()) {
- throw new IllegalStateException("Internal cache not yet initialized");
- }
- final ObjectSpecification objectSpecification = cache.getByObjectType(objectSpecId);
- if(objectSpecification == null) {
- // fallback
- return loadSpecification(objectSpecId.asString(), IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
- }
- return objectSpecification;
- }
-
- public IsisConfiguration getConfiguration() {
- return _Config.getConfiguration();
- }
+ List<ObjectSpecification> allSpecifications();
+
+ ObjectSpecification lookupBySpecId(ObjectSpecId objectSpecId);
+
+ // -- LOADING
+
+ void reloadSpecification(Class<?> domainType);
+
+ /**
+ * Return the specification for the specified class of object.
+ *
+ * <p>
+ * It is possible for this method to return <tt>null</tt>, for example if
+ * the configured {@link org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutor}
+ * has filtered out the class.
+ *
+ * @return {@code null} if {@code domainType==null}
+ */
+ ObjectSpecification loadSpecification(@Nullable Class<?> domainType, IntrospectionState upTo);
+
+ /**
+ * Return the specification for the specified ObjectSpecId.
+ *
+ * <p>
+ * It is possible for this method to return <tt>null</tt>, for example if
+ * the configured {@link org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutor}
+ * has filtered out the class.
+ *
+ * @return {@code null} if {@code objectSpecId==null}
+ */
+ ObjectSpecification loadSpecification(@Nullable ObjectSpecId objectSpecId, IntrospectionState upTo);
+
+ // -- SHORTCUTS
+
+ default ObjectSpecification loadSpecification(@Nullable final Class<?> domainType) {
+ return loadSpecification(domainType, IntrospectionState.TYPE_INTROSPECTED);
+ }
+
+ default ObjectSpecification loadSpecification(@Nullable ObjectSpecId objectSpecId) {
+ return loadSpecification(objectSpecId, IntrospectionState.TYPE_INTROSPECTED);
+ }
+
+
}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault.java
new file mode 100644
index 0000000..59781c2
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault.java
@@ -0,0 +1,606 @@
+package org.apache.isis.core.metamodel.specloader;
+
+import static org.apache.isis.commons.internal.base._With.requires;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.annotation.Nullable;
+import javax.enterprise.inject.Vetoed;
+
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.apache.isis.applib.services.registry.ServiceRegistry.BeanAdapter;
+import org.apache.isis.commons.internal.base._NullSafe;
+import org.apache.isis.commons.internal.base._Strings;
+import org.apache.isis.commons.internal.collections._Lists;
+import org.apache.isis.commons.internal.context._Context;
+import org.apache.isis.commons.internal.debug._Probe;
+import org.apache.isis.config.IsisConfiguration;
+import org.apache.isis.config.internal._Config;
+import org.apache.isis.config.registry.IsisBeanTypeRegistry;
+import org.apache.isis.core.commons.exceptions.IsisException;
+import org.apache.isis.core.commons.lang.ClassUtil;
+import org.apache.isis.core.metamodel.MetaModelContext;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
+import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
+import org.apache.isis.core.metamodel.spec.FreeStandingList;
+import org.apache.isis.core.metamodel.spec.ObjectSpecId;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutor;
+import org.apache.isis.core.metamodel.specloader.facetprocessor.FacetProcessor;
+import org.apache.isis.core.metamodel.specloader.postprocessor.PostProcessor;
+import org.apache.isis.core.metamodel.specloader.specimpl.FacetedMethodsBuilderContext;
+import org.apache.isis.core.metamodel.specloader.specimpl.IntrospectionState;
+import org.apache.isis.core.metamodel.specloader.specimpl.ObjectSpecificationAbstract;
+import org.apache.isis.core.metamodel.specloader.specimpl.dflt.ObjectSpecificationDefault;
+import org.apache.isis.core.metamodel.specloader.specimpl.standalonelist.ObjectSpecificationOnStandaloneList;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelDeficiencies;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidator;
+import org.apache.isis.core.metamodel.specloader.validator.ValidationFailures;
+import org.apache.isis.core.runtime.threadpool.ThreadPoolExecutionMode;
+import org.apache.isis.core.runtime.threadpool.ThreadPoolSupport;
+import org.apache.isis.progmodels.dflt.ProgrammingModelFacetsJava5;
+import org.apache.isis.schema.utils.CommonDtoUtils;
+
+import lombok.val;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * <p>
+ * The implementation provides for a degree of pluggability:
+ * <ul>
+ * <li>The most important plug-in point is {@link ProgrammingModel} that
+ * specifies the set of {@link Facet} that make up programming model. If not
+ * specified then defaults to {@link ProgrammingModelFacetsJava5} (which should
+ * be used as a starting point for your own customizations).
+ * <li>The only mandatory plug-in point is {@link ClassSubstitutor}, which
+ * allows the class to be loaded to be substituted if required. This is used in
+ * conjunction with some <tt>PersistenceMechanism</tt>s that do class
+ * enhancement.
+ * </ul>
+ * </p>
+ */
+@Vetoed // has a producer
+@Slf4j
+public class SpecificationLoaderDefault implements SpecificationLoader {
+
+ private final ClassSubstitutor classSubstitutor = new ClassSubstitutor();
+
+ private final ProgrammingModel programmingModel;
+ private final FacetProcessor facetProcessor;
+
+
+ private final MetaModelValidator metaModelValidator;
+ private final SpecificationCacheDefault cache = new SpecificationCacheDefault();
+ private final PostProcessor postProcessor;
+
+
+ public SpecificationLoaderDefault(
+ final ProgrammingModel programmingModel,
+ final MetaModelValidator metaModelValidator) {
+
+ this.programmingModel = programmingModel;
+ this.metaModelValidator = metaModelValidator;
+
+ this.facetProcessor = new FacetProcessor(programmingModel);
+ this.postProcessor = new PostProcessor(programmingModel);
+ }
+
+ // -- LIVE CYCLE
+
+ /**
+ * Initializes and wires up, and primes the cache based on any service
+ * classes (provided by the {@link ServicesInjector}).
+ */
+ @Override
+ public void init() {
+
+ if (log.isDebugEnabled()) {
+ log.debug("initialising {}", this);
+ }
+
+ // wire subcomponents into each other
+ //facetProcessor.setServicesInjector(servicesInjector);
+
+ // initialize subcomponents
+ this.programmingModel.init();
+ facetProcessor.init();
+
+ postProcessor.init();
+ metaModelValidator.init();
+
+
+ // need to completely load services and mixins (synchronously)
+ log.info("Loading all specs (up to state of {})", IntrospectionState.NOT_INTROSPECTED);
+
+ val typeRegistry = IsisBeanTypeRegistry.current();
+
+ final List<ObjectSpecification> specificationsFromRegistry = _Lists.newArrayList();
+
+ // we use allServiceClasses() - obtained from servicesInjector - rather than reading from the
+ // AppManifest.Registry.instance().getDomainServiceTypes(), because the former also has the fallback
+ // services set up in IsisSessionFactoryBuilder beforehand.
+ final List<ObjectSpecification> domainServiceSpecs =
+ loadSpecificationsForBeans(
+ streamBeans(), NatureOfService.DOMAIN,
+ specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
+ );
+ final List<ObjectSpecification> mixinSpecs =
+ loadSpecificationsFor(
+ typeRegistry.getMixinTypes().stream(), null,
+ specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
+ );
+ loadSpecificationsFor(
+ CommonDtoUtils.VALUE_TYPES.stream(), null,
+ specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
+ );
+ loadSpecificationsFor(
+ typeRegistry.getDomainObjectTypes().stream(), null,
+ specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
+ );
+ loadSpecificationsFor(
+ typeRegistry.getViewModelTypes().stream(), null,
+ specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
+ );
+ loadSpecificationsFor(
+ typeRegistry.getXmlElementTypes().stream(), null,
+ specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
+ );
+
+ cache.init();
+
+ final Collection<ObjectSpecification> cachedSpecifications = allCachedSpecifications();
+
+ logBefore(specificationsFromRegistry, cachedSpecifications);
+
+ log.info("Introspecting all specs up to {}", IntrospectionState.TYPE_INTROSPECTED);
+ introspect(specificationsFromRegistry, IntrospectionState.TYPE_INTROSPECTED);
+
+ log.info("Introspecting domainService specs up to {}", IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+ introspect(domainServiceSpecs, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+
+ log.info("Introspecting mixin specs up to {}", IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+ introspect(mixinSpecs, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+
+ logAfter(cachedSpecifications);
+
+ final IntrospectionMode mode = CONFIG_PROPERTY_MODE.from(getConfiguration());
+ if(mode.isFullIntrospect(_Context.getEnvironment().getDeploymentType())) {
+ log.info("Introspecting all cached specs up to {}", IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+ introspect(cachedSpecifications, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+ }
+
+ log.info("init() - done");
+
+ //FIXME [2033] remove debug code ...
+ //{
+ // streamServiceClasses()
+ // .forEach(service->probe.println("using service %s", service));
+ //
+ //
+ // val metaModelService = _CDI.getSingleton(MetaModelService.class);
+ // val jaxbService = _CDI.getSingleton(JaxbService.class);
+ //
+ // val metamodelDto =
+ // metaModelService.exportMetaModel(
+ // new MetaModelService.Config()
+ // .withIgnoreNoop()
+ // .withIgnoreAbstractClasses()
+ // .withIgnoreBuiltInValueTypes()
+ // .withIgnoreInterfaces()
+ // .withPackagePrefix("domainapp")
+ // );
+ //
+ // final String xml = jaxbService.toXml(metamodelDto);
+ // //probe.println(xml);
+ // }
+ }
+
+ @Override
+ public void shutdown() {
+ cache.clear();
+
+ log.info("shutting down {}", this);
+ }
+
+ // -- VALIDATION
+
+ private ValidationFailures validationFailures;
+
+ private MetaModelDeficiencies validateThenGetDeficienciesIfAny() {
+ final IntrospectionMode mode = CONFIG_PROPERTY_MODE.from(getConfiguration());
+ if(!mode.isFullIntrospect(_Context.getEnvironment().getDeploymentType())) {
+ log.info("Meta model validation skipped (full introspection of metamodel not configured)");
+ return null;
+ }
+
+ ValidationFailures validationFailures = validate();
+ return validationFailures.getDeficienciesIfAny();
+ }
+
+ private ValidationFailures validate() {
+ if(validationFailures == null) {
+ validationFailures = new ValidationFailures();
+ metaModelValidator.validate(validationFailures);
+ }
+ return validationFailures;
+ }
+
+ // -- SPEC LOADING
+
+ @Override
+ public void reloadSpecification(Class<?> domainType) {
+ invalidateCache(domainType);
+ loadSpecification(domainType);
+ }
+
+ @Override
+ public ObjectSpecification loadSpecification(
+ @Nullable final ObjectSpecId objectSpecId,
+ final IntrospectionState upTo) {
+
+ if(objectSpecId==null) {
+ return null;
+ }
+
+ val className = objectSpecId.asString();
+
+ if(_Strings.isNullOrEmpty(className)) {
+ return null;
+ }
+
+ try {
+ final Class<?> type = loadClassByName(className);
+ return loadSpecification(type, upTo);
+
+ } catch (final ClassNotFoundException e) {
+ final ObjectSpecification spec = cache.get(className);
+ if (spec == null) {
+ throw new IsisException("No such class available: " + className);
+ }
+ return spec;
+ }
+ }
+
+ @Override
+ public ObjectSpecification loadSpecification(@Nullable final Class<?> type, final IntrospectionState upTo) {
+
+ if(type==null) {
+ return null;
+ }
+
+ requires(upTo, "upTo");
+
+ val spec = internalLoadSpecification(type, null, upTo);
+ if(spec == null) {
+ return null;
+ }
+
+ // TODO: review, is this now needed?
+ // We now create the ObjectSpecIdFacet immediately after creating the ObjectSpecification,
+ // so the cache shouldn't need updating here also.
+ if(cache.isInitialized()) {
+ // umm. It turns out that anonymous inner classes (eg org.estatio.dom.WithTitleGetter$ToString$1)
+ // don't have an ObjectSpecId; hence the guard.
+ if(spec.containsDoOpFacet(ObjectSpecIdFacet.class)) {
+ ObjectSpecId specId = spec.getSpecId();
+ if (cache.getByObjectType(specId) == null) {
+ cache.recache(spec);
+ }
+ }
+ }
+ return spec;
+ }
+
+ private ObjectSpecification internalLoadSpecification(
+ final Class<?> type,
+ final NatureOfService natureFallback,
+ final IntrospectionState upTo) {
+
+ final Class<?> substitutedType = classSubstitutor.getClass(type);
+ if (substitutedType == null) {
+ return null;
+ }
+
+ final String typeName = substitutedType.getName();
+ ObjectSpecification spec = cache.get(typeName);
+ if (spec != null) {
+ return spec;
+ }
+
+ //TODO[2033] don't block on long running code ... 'specSpi.introspectUpTo(upTo);'
+ synchronized (this) {
+
+ spec = cache.get(typeName);
+ if (spec != null) {
+ return spec;
+ }
+
+ final ObjectSpecification specification = createSpecification(substitutedType, natureFallback);
+
+ // put into the cache prior to introspecting, to prevent
+ // infinite loops
+ cache.cache(typeName, specification);
+
+ final ObjectSpecificationAbstract specSpi = (ObjectSpecificationAbstract) specification;
+ specSpi.introspectUpTo(upTo);
+
+ return specification;
+ }
+ }
+
+ // -- LOOKUP
+
+ @Override
+ public List<ObjectSpecification> allSpecifications() {
+ return _Lists.newArrayList(allCachedSpecifications());
+ }
+
+ @Override
+ public ObjectSpecification lookupBySpecId(ObjectSpecId objectSpecId) {
+ if(!cache.isInitialized()) {
+ throw new IllegalStateException("Internal cache not yet initialized");
+ }
+ final ObjectSpecification objectSpecification = cache.getByObjectType(objectSpecId);
+ if(objectSpecification == null) {
+ // fallback
+ return loadSpecification(objectSpecId, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+ }
+ return objectSpecification;
+ }
+
+
+
+ // -- HELPER
+
+ private IsisConfiguration getConfiguration() {
+ return _Config.getConfiguration();
+ }
+
+ private Collection<ObjectSpecification> allCachedSpecifications() {
+ return cache.allSpecifications();
+ }
+
+ private Stream<BeanAdapter> streamBeans() {
+ final ServiceRegistry registry = MetaModelContext.current().getServiceRegistry();
+ return registry.streamRegisteredBeans();
+ }
+
+ /**
+ * Creates the appropriate type of {@link ObjectSpecification}.
+ */
+ private ObjectSpecification createSpecification(
+ final Class<?> cls,
+ final NatureOfService fallback) {
+
+ // ... and create the specs
+ final ObjectSpecificationAbstract objectSpec;
+ if (FreeStandingList.class.isAssignableFrom(cls)) {
+
+ objectSpec = new ObjectSpecificationOnStandaloneList(facetProcessor, postProcessor);
+
+ } else {
+
+ final FacetedMethodsBuilderContext facetedMethodsBuilderContext =
+ new FacetedMethodsBuilderContext(
+ this, facetProcessor);
+
+ final NatureOfService natureOfServiceIfAny = natureOfServiceFrom(cls, fallback);
+
+ objectSpec = new ObjectSpecificationDefault(cls,
+ facetedMethodsBuilderContext,
+ facetProcessor, natureOfServiceIfAny, postProcessor);
+ }
+
+ return objectSpec;
+ }
+
+ private NatureOfService natureOfServiceFrom(
+ final Class<?> type,
+ final NatureOfService fallback) {
+ final DomainService domainServiceIfAny = type.getAnnotation(DomainService.class);
+ return domainServiceIfAny != null ? domainServiceIfAny.nature() : fallback;
+ }
+
+ private Class<?> loadClassByName(final String className) throws ClassNotFoundException {
+ final Class<?> builtIn = ClassUtil.getBuiltIn(className);
+ if (builtIn != null) {
+ return builtIn;
+ }
+ return ClassUtil.forName(className);
+ }
+
+ private final static _Probe probe = _Probe.unlimited().label("SpecificationLoader");
+
+ private void logBefore(
+ final List<ObjectSpecification> specificationsFromRegistry,
+ final Collection<ObjectSpecification> cachedSpecifications) {
+ if(!log.isDebugEnabled()) {
+ return;
+ }
+ log.debug(String.format(
+ "specificationsFromRegistry.size = %d ; cachedSpecifications.size = %d",
+ specificationsFromRegistry.size(), cachedSpecifications.size()));
+
+ List<ObjectSpecification> registryNotCached = specificationsFromRegistry.stream()
+ .filter(spec -> !cachedSpecifications.contains(spec))
+ .collect(Collectors.toList());
+ List<ObjectSpecification> cachedNotRegistry = cachedSpecifications.stream()
+ .filter(spec -> !specificationsFromRegistry.contains(spec))
+ .collect(Collectors.toList());
+
+ log.debug(String.format(
+ "registryNotCached.size = %d ; cachedNotRegistry.size = %d",
+ registryNotCached.size(), cachedNotRegistry.size()));
+ }
+
+ private void logAfter(final Collection<ObjectSpecification> cachedSpecifications) {
+ if(!log.isDebugEnabled()) {
+ return;
+ }
+
+ final Collection<ObjectSpecification> cachedSpecificationsAfter = cache.allSpecifications();
+ List<ObjectSpecification> cachedAfterNotBefore = cachedSpecificationsAfter.stream()
+ .filter(spec -> !cachedSpecifications.contains(spec))
+ .collect(Collectors.toList());
+ log.debug(String.format("cachedSpecificationsAfter.size = %d ; cachedAfterNotBefore.size = %d",
+ cachedSpecificationsAfter.size(), cachedAfterNotBefore.size()));
+ }
+
+ private void introspect(final Collection<ObjectSpecification> specs, final IntrospectionState upTo) {
+ final List<Callable<Object>> callables = _Lists.newArrayList();
+ for (final ObjectSpecification specification : specs) {
+ Callable<Object> callable = new Callable<Object>() {
+ @Override
+ public Object call() {
+
+ final ObjectSpecificationAbstract specSpi = (ObjectSpecificationAbstract) specification;
+ specSpi.introspectUpTo(upTo);
+
+ return null;
+ }
+ public String toString() {
+ return String.format(
+ "%s: #introspectUpTo( %s )",
+ specification.getFullIdentifier(), upTo);
+ }
+ };
+ callables.add(callable);
+ }
+
+ invokeAndWait(callables);
+ }
+
+ private void invokeAndWait(final List<Callable<Object>> callables) {
+ final ThreadPoolSupport threadPoolSupport = ThreadPoolSupport.getInstance();
+ final boolean parallelize = CONFIG_PROPERTY_PARALLELIZE.from(getConfiguration());
+
+ final ThreadPoolExecutionMode executionModeFromConfig = parallelize
+ ? ThreadPoolExecutionMode.PARALLEL
+ : ThreadPoolExecutionMode.SEQUENTIAL;
+
+ final List<Future<Object>> futures =
+ threadPoolSupport.invokeAll(executionModeFromConfig, callables);
+ threadPoolSupport.joinGatherFailures(futures);
+ }
+
+ private List<ObjectSpecification> loadSpecificationsFor(
+ final Stream<Class<?>> domainTypes,
+ final NatureOfService natureOfServiceFallback,
+ final List<ObjectSpecification> appendTo,
+ final IntrospectionState upTo) {
+
+ return domainTypes
+ .map(domainType->internalLoadSpecification(domainType, natureOfServiceFallback, upTo))
+ .filter(_NullSafe::isPresent)
+ .peek(appendTo::add)
+ .collect(Collectors.toList());
+ }
+
+ private List<ObjectSpecification> loadSpecificationsForBeans (
+ final Stream<BeanAdapter> beans,
+ final NatureOfService natureOfServiceFallback,
+ final List<ObjectSpecification> appendTo,
+ final IntrospectionState upTo) {
+
+ return beans
+ .filter(bean->bean.isDomainService())
+ .map(bean->bean.getBean().getBeanClass())
+ .map(domainType->internalLoadSpecification(domainType, natureOfServiceFallback, upTo))
+ .filter(_NullSafe::isPresent)
+ .peek(appendTo::add)
+ .collect(Collectors.toList());
+ }
+
+ private void invalidateCache(final Class<?> cls) {
+
+ if(!cache.isInitialized()) {
+ // could be called by JRebel plugin, before we are up-and-running
+ // just ignore.
+ return;
+ }
+ final Class<?> substitutedType = classSubstitutor.getClass(cls);
+
+ if(substitutedType.isAnonymousClass()) {
+ // JRebel plugin might call us... just ignore 'em.
+ return;
+ }
+
+ ObjectSpecification spec = loadSpecification(substitutedType, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+ while(spec != null) {
+ final Class<?> type = spec.getCorrespondingClass();
+ cache.remove(type.getName());
+ if(spec.containsDoOpFacet(ObjectSpecIdFacet.class)) {
+ // umm. Some specs do not have an ObjectSpecIdFacet...
+ recache(spec);
+ }
+ spec = spec.superclass();
+ }
+ }
+
+
+ private void recache(final ObjectSpecification newSpec) {
+ cache.recache(newSpec);
+ }
+
+ // -- DEPRECATED
+
+// /**
+// * Loads the specifications of the specified types except the one specified
+// * (to prevent an infinite loop).
+// */
+// public boolean loadSpecifications(
+// final List<Class<?>> typesToLoad,
+// final Class<?> typeToIgnore,
+// final IntrospectionState upTo) {
+//
+// boolean anyLoadedAsNull = false;
+// for (final Class<?> typeToLoad : typesToLoad) {
+// if (typeToLoad != typeToIgnore) {
+// final ObjectSpecification objectSpecification =
+// internalLoadSpecification(typeToLoad, null, upTo);
+// final boolean loadedAsNull = (objectSpecification == null);
+// anyLoadedAsNull = loadedAsNull || anyLoadedAsNull;
+// }
+// }
+// return anyLoadedAsNull;
+// }
+
+// public ObjectSpecification peekSpecification(final Class<?> type) {
+//
+// final Class<?> substitutedType = classSubstitutor.getClass(type);
+// if (substitutedType == null) {
+// return null;
+// }
+//
+// final String typeName = substitutedType.getName();
+// ObjectSpecification spec = cache.get(typeName);
+// if (spec != null) {
+// return spec;
+// }
+//
+// return null;
+// }
+
+// /**
+// * Whether this class has been loaded.
+// */
+// private boolean loaded(final Class<?> cls) {
+// return loaded(cls.getName());
+// }
+
+// /**
+// * @see #loaded(Class).
+// */
+// private boolean loaded(final String fullyQualifiedClassName) {
+// return cache.get(fullyQualifiedClassName) != null;
+// }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/FacetedMethodsBuilder.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/FacetedMethodsBuilder.java
index 098300d..994d778 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/FacetedMethodsBuilder.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/FacetedMethodsBuilder.java
@@ -20,18 +20,15 @@
package org.apache.isis.core.metamodel.specloader.specimpl;
import java.lang.reflect.Method;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import org.apache.isis.applib.annotation.Action;
import org.apache.isis.commons.internal.collections._Lists;
+import org.apache.isis.commons.internal.collections._Sets;
import org.apache.isis.config.internal._Config;
import org.apache.isis.core.commons.exceptions.IsisException;
import org.apache.isis.core.commons.lang.ListExtensions;
@@ -53,6 +50,10 @@ import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
import org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutor;
import org.apache.isis.core.metamodel.specloader.facetprocessor.FacetProcessor;
import org.apache.isis.core.metamodel.specloader.traverser.SpecificationTraverser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import lombok.val;
public class FacetedMethodsBuilder {
@@ -234,12 +235,15 @@ public class FacetedMethodsBuilder {
final Set<Method> associationCandidateMethods = getFacetProcessor().findAssociationCandidateAccessors(methods, new HashSet<Method>());
// Ensure all return types are known
- final List<Class<?>> typesToLoad = _Lists.newArrayList();
+ final Set<Class<?>> typesToLoad = _Sets.newHashSet();
for (final Method method : associationCandidateMethods) {
specificationTraverser.traverseTypes(method, typesToLoad);
}
- getSpecificationLoader().loadSpecifications(typesToLoad, introspectedClass,
- IntrospectionState.TYPE_INTROSPECTED);
+ typesToLoad.remove(introspectedClass);
+
+ val specLoader = getSpecificationLoader();
+ val upTo = IntrospectionState.TYPE_INTROSPECTED;
+ typesToLoad.forEach(typeToLoad->specLoader.loadSpecification(typeToLoad, upTo));
// now create FacetedMethods for collections and for properties
final List<FacetedMethod> associationFacetedMethods = _Lists.newArrayList();
@@ -438,11 +442,16 @@ public class FacetedMethodsBuilder {
return false;
}
- final List<Class<?>> typesToLoad = new ArrayList<Class<?>>();
+ final Set<Class<?>> typesToLoad = _Sets.newHashSet();
specificationTraverser.traverseTypes(actionMethod, typesToLoad);
+
+ val specLoader = getSpecificationLoader();
+ val upTo = IntrospectionState.TYPE_INTROSPECTED;
+
+ val anyLoadedAsNull = typesToLoad.stream()
+ .map(typeToLoad->specLoader.loadSpecification(typeToLoad, upTo))
+ .anyMatch(spec->spec==null);
- final boolean anyLoadedAsNull = getSpecificationLoader()
- .loadSpecifications(typesToLoad, null, IntrospectionState.TYPE_INTROSPECTED);
if (anyLoadedAsNull) {
return false;
}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/IntrospectionState.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/IntrospectionState.java
index e200ef5..1c28510 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/IntrospectionState.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/IntrospectionState.java
@@ -26,6 +26,7 @@ public enum IntrospectionState implements Comparable<IntrospectionState> {
* 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).
*/
@@ -35,6 +36,7 @@ public enum IntrospectionState implements Comparable<IntrospectionState> {
* Interim stage, to avoid infinite loops while on way to being {@link #TYPE_AND_MEMBERS_INTROSPECTED}
*/
MEMBERS_BEING_INTROSPECTED,
+
/**
* Fully introspected... class and also its members.
*/
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
index f8ee912..9faf81f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
@@ -622,8 +622,6 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
}
- private static ThreadLocal<Boolean> invalidatingCache = ThreadLocal.withInitial(() -> Boolean.FALSE);
-
@Override
public ObjectMember getMember(final String memberId) {
introspectUpTo(IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
@@ -639,6 +637,9 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
return null;
}
+ //TODO [2033] remove or replace
+// private static ThreadLocal<Boolean> invalidatingCache = ThreadLocal.withInitial(() -> Boolean.FALSE);
+
/**
* The association with the given {@link ObjectAssociation#getId() id}.
@@ -662,27 +663,28 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
if(oa != null) {
return oa;
}
- if(_Context.isPrototyping()) {
- // automatically refresh if not in production
- // (better support for jrebel)
-
- LOG.warn("Could not find association with id '{}'; invalidating cache automatically", id);
- if(!invalidatingCache.get()) {
- // make sure don't go into an infinite loop, though.
- try {
- invalidatingCache.set(true);
- getSpecificationLoader().invalidateCache(getCorrespondingClass());
- } finally {
- invalidatingCache.set(false);
- }
- } else {
- LOG.warn("... already invalidating cache earlier in stacktrace, so skipped this time");
- }
- oa = getAssociationWithId(id);
- if(oa != null) {
- return oa;
- }
- }
+//TODO [2033] remove or replace
+// if(_Context.isPrototyping()) {
+// // automatically refresh if not in production
+// // (better support for jrebel)
+//
+// LOG.warn("Could not find association with id '{}'; invalidating cache automatically", id);
+// if(!invalidatingCache.get()) {
+// // make sure don't go into an infinite loop, though.
+// try {
+// invalidatingCache.set(true);
+// getSpecificationLoader().invalidateCache(getCorrespondingClass());
+// } finally {
+// invalidatingCache.set(false);
+// }
+// } else {
+// LOG.warn("... already invalidating cache earlier in stacktrace, so skipped this time");
+// }
+// oa = getAssociationWithId(id);
+// if(oa != null) {
+// return oa;
+// }
+// }
throw new ObjectSpecificationException(
String.format("No association called '%s' in '%s'", id, getSingularName()));
}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/traverser/SpecificationTraverser.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/traverser/SpecificationTraverser.java
index 4e25b78..7ee8317 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/traverser/SpecificationTraverser.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/traverser/SpecificationTraverser.java
@@ -20,7 +20,7 @@
package org.apache.isis.core.metamodel.specloader.traverser;
import java.lang.reflect.Method;
-import java.util.List;
+import java.util.Collection;
public class SpecificationTraverser {
@@ -31,7 +31,7 @@ public class SpecificationTraverser {
* It's possible for there to be multiple return types: the generic type,
* and the parameterized type.
*/
- public void traverseTypes(final Method method, final List<Class<?>> discoveredTypes) {
+ public void traverseTypes(final Method method, final Collection<Class<?>> discoveredTypes) {
final TypeExtractorMethodReturn returnTypes = new TypeExtractorMethodReturn(method);
for (final Class<?> returnType : returnTypes) {
discoveredTypes.add(returnType);
diff --git a/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/JavaReflectorHelper.java b/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/JavaReflectorHelper.java
index a5a3676..e897c9c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/JavaReflectorHelper.java
+++ b/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/JavaReflectorHelper.java
@@ -24,6 +24,7 @@ import java.util.Collection;
import org.apache.isis.core.metamodel.facetapi.MetaModelRefiner;
import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoaderDefault;
import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidator;
import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorComposite;
@@ -44,7 +45,7 @@ public final class JavaReflectorHelper {
programmingModel.refineMetaModelValidator(metaModelValidator);
- return new SpecificationLoader(programmingModel, metaModelValidator);
+ return new SpecificationLoaderDefault(programmingModel, metaModelValidator);
}
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Command.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Command.java
index 28e28b0..92edc63 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Command.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Command.java
@@ -15,7 +15,6 @@ import org.apache.isis.applib.annotation.CommandReification;
import org.apache.isis.applib.annotation.SemanticsOf;
import org.apache.isis.core.metamodel.facetapi.Facet;
import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessMethodContext;
-import org.apache.isis.core.metamodel.facets.actions.action.ActionAnnotationFacetFactoryTest.SomeTransactionalId;
import org.apache.isis.core.metamodel.facets.actions.action.command.CommandFacetForActionAnnotation;
import org.apache.isis.core.metamodel.facets.actions.action.command.CommandFacetForActionAnnotationAsConfigured;
import org.apache.isis.core.metamodel.facets.actions.action.command.CommandFacetFromConfiguration;
@@ -74,7 +73,7 @@ public class ActionAnnotationFacetFactoryTest_Command extends ActionAnnotationFa
// then
final Facet facet = facetedMethod.getFacet(CommandFacet.class);
assertNotNull(facet);
- assert(facet instanceof CommandFacetFromConfiguration);
+ assertTrue(facet instanceof CommandFacetFromConfiguration);
final CommandFacetFromConfiguration facetImpl = (CommandFacetFromConfiguration) facet;
assertThat(facetImpl.persistence(), is(org.apache.isis.applib.annotation.CommandPersistence.PERSISTED));
assertThat(facetImpl.executeIn(), is(org.apache.isis.applib.annotation.CommandExecuteIn.FOREGROUND));
@@ -130,9 +129,7 @@ public class ActionAnnotationFacetFactoryTest_Command extends ActionAnnotationFa
public void given_asConfigured_and_configurationSetToIgnoreQueryOnly_andSafeSemantics_thenNone() {
class Customer {
- @Action(
- command = CommandReification.AS_CONFIGURED
- )
+ @Action(command = CommandReification.AS_CONFIGURED)
public void someAction() {
}
}
@@ -182,9 +179,7 @@ public class ActionAnnotationFacetFactoryTest_Command extends ActionAnnotationFa
public void given_asConfigured_and_configurationSetToIgnoreQueryOnly_andNoSemantics_thenException() {
class Customer {
- @Action(
- command = CommandReification.AS_CONFIGURED
- )
+ @Action(command = CommandReification.AS_CONFIGURED)
public void someAction() {
}
}
@@ -199,9 +194,7 @@ public class ActionAnnotationFacetFactoryTest_Command extends ActionAnnotationFa
public void given_asConfigured_and_configurationSetToNone_thenNone() {
class Customer {
- @Action(
- command = CommandReification.AS_CONFIGURED
- )
+ @Action(command = CommandReification.AS_CONFIGURED)
public void someAction() {
}
}
@@ -248,9 +241,7 @@ public class ActionAnnotationFacetFactoryTest_Command extends ActionAnnotationFa
// given
class Customer {
- @Action(
- command = CommandReification.ENABLED
- )
+ @Action(command = CommandReification.ENABLED)
public void someAction() {
}
}
@@ -273,9 +264,7 @@ public class ActionAnnotationFacetFactoryTest_Command extends ActionAnnotationFa
// given
class Customer {
- @Action(
- command = CommandReification.DISABLED
- )
+ @Action(command = CommandReification.DISABLED)
public void someAction() {
}
}
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Invocation.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Invocation.java
index 43e85ba..31bec29 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Invocation.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Invocation.java
@@ -25,7 +25,8 @@ public class ActionAnnotationFacetFactoryTest_Invocation extends ActionAnnotatio
class Customer {
- class SomeActionInvokedDomainEvent extends ActionDomainEvent<Customer> { }
+ class SomeActionInvokedDomainEvent extends ActionDomainEvent<Customer> {
+ private static final long serialVersionUID = 1L; }
@Action(domainEvent = SomeActionInvokedDomainEvent.class)
public void someAction() {
@@ -66,7 +67,8 @@ public class ActionAnnotationFacetFactoryTest_Invocation extends ActionAnnotatio
class Customer {
- class SomeActionInvokedDomainEvent extends ActionDomainEvent<Customer> { }
+ class SomeActionInvokedDomainEvent extends ActionDomainEvent<Customer> {
+ private static final long serialVersionUID = 1L; }
@Action(domainEvent = SomeActionInvokedDomainEvent.class)
public void someAction() {
@@ -109,7 +111,7 @@ public class ActionAnnotationFacetFactoryTest_Invocation extends ActionAnnotatio
class Customer {
class SomeActionInvokedDomainEvent extends ActionDomainEvent<Customer> {
- }
+ private static final long serialVersionUID = 1L; }
@Action(domainEvent= SomeActionInvokedDomainEvent.class)
public void someAction() {
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Publishing.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Publishing.java
index c7eee75..f7337fb 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Publishing.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Publishing.java
@@ -8,9 +8,9 @@ import java.lang.reflect.Method;
import org.apache.isis.applib.annotation.Action;
import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.commons.internal.base._Blackhole;
import org.apache.isis.core.metamodel.facetapi.Facet;
import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessMethodContext;
-import org.apache.isis.core.metamodel.facets.actions.action.ActionAnnotationFacetFactoryTest.SomeTransactionalId;
import org.apache.isis.core.metamodel.facets.actions.action.publishing.PublishedActionFacetForActionAnnotation;
import org.apache.isis.core.metamodel.facets.actions.action.publishing.PublishedActionFacetFromConfiguration;
import org.apache.isis.core.metamodel.facets.actions.publish.PublishedActionFacet;
@@ -67,6 +67,7 @@ public class ActionAnnotationFacetFactoryTest_Publishing extends ActionAnnotatio
final Facet facet = facetedMethod.getFacet(PublishedActionFacet.class);
assertNotNull(facet);
final PublishedActionFacetFromConfiguration facetImpl = (PublishedActionFacetFromConfiguration) facet;
+ _Blackhole.consume(facetImpl);
}
@Test(expected=IllegalStateException.class)
@@ -165,7 +166,8 @@ public class ActionAnnotationFacetFactoryTest_Publishing extends ActionAnnotatio
final Facet facet = facetedMethod.getFacet(PublishedActionFacet.class);
assertNotNull(facet);
final PublishedActionFacetForActionAnnotation facetImpl = (PublishedActionFacetForActionAnnotation) facet;
-
+ _Blackhole.consume(facetImpl);
+
expectNoMethodsRemoved();
}
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderTestAbstract.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderTestAbstract.java
index 3963d2e..b0343cb 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderTestAbstract.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderTestAbstract.java
@@ -90,7 +90,7 @@ abstract class SpecificationLoaderTestAbstract {
@Produces
SpecificationLoader getSpecificationLoader() {
- return new SpecificationLoader(
+ return new SpecificationLoaderDefault(
new ProgrammingModelFacetsJava5(DeprecatedPolicy.HONOUR),
new MetaModelValidatorDefault());
}
diff --git a/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundServiceDefault.java b/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundServiceDefault.java
index 8f7e57e..7b37147 100644
--- a/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundServiceDefault.java
+++ b/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundServiceDefault.java
@@ -30,8 +30,8 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.services.background.BackgroundCommandService;
-import org.apache.isis.applib.services.background.BackgroundService;
+import org.apache.isis.applib.services.background.CommandSchedulerService;
+import org.apache.isis.applib.services.background.BackgroundExecutionService;
import org.apache.isis.applib.services.command.CommandContext;
import org.apache.isis.applib.services.factory.FactoryService;
import org.apache.isis.commons.internal._Constants;
@@ -41,18 +41,14 @@ import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
import org.apache.isis.core.metamodel.specloader.classsubstitutor.ProxyEnhanced;
import org.apache.isis.core.plugins.codegen.ProxyFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* For command-reification depends on an implementation of
- * {@link org.apache.isis.applib.services.background.BackgroundCommandService} to
+ * {@link org.apache.isis.applib.services.background.CommandSchedulerService} to
* be configured.
*/
@Singleton
-public class BackgroundServiceDefault implements BackgroundService {
-
- static final Logger LOG = LoggerFactory.getLogger(BackgroundServiceDefault.class);
+public class BackgroundServiceDefault implements BackgroundExecutionService {
// only used if there is no BackgroundCommandService
static class BuiltinExecutor {
@@ -172,7 +168,7 @@ public class BackgroundServiceDefault implements BackgroundService {
// //////////////////////////////////////
- @Inject @Any private Instance<BackgroundCommandService> backgroundCommandServices;
+ @Inject @Any private Instance<CommandSchedulerService> backgroundCommandServices;
@Inject private CommandDtoServiceInternal commandDtoServiceInternal;
@Inject private CommandContext commandContext;
@Inject private FactoryService factoryService;
diff --git a/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/background/CommandInvocationHandler.java b/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/background/CommandInvocationHandler.java
index 36295bb..1c1287a 100644
--- a/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/background/CommandInvocationHandler.java
+++ b/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/background/CommandInvocationHandler.java
@@ -25,7 +25,7 @@ import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;
-import org.apache.isis.applib.services.background.BackgroundCommandService;
+import org.apache.isis.applib.services.background.CommandSchedulerService;
import org.apache.isis.applib.services.command.Command;
import org.apache.isis.applib.services.command.CommandContext;
import org.apache.isis.core.metamodel.MetaModelContext;
@@ -44,7 +44,7 @@ import org.apache.isis.schema.cmd.v1.CommandDto;
class CommandInvocationHandler<T> implements InvocationHandler {
- private final BackgroundCommandService backgroundCommandService;
+ private final CommandSchedulerService backgroundCommandService;
private final T target;
private final Object mixedInIfAny;
private final SpecificationLoader specificationLoader;
@@ -53,7 +53,7 @@ class CommandInvocationHandler<T> implements InvocationHandler {
private final Supplier<ObjectAdapterProvider> adapterProviderSupplier;
CommandInvocationHandler(
- BackgroundCommandService backgroundCommandService,
+ CommandSchedulerService backgroundCommandService,
T target,
Object mixedInIfAny,
SpecificationLoader specificationLoader,
diff --git a/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/background/ForkingInvocationHandler.java b/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/background/ForkingInvocationHandler.java
index 175f3bd..8ef5388 100644
--- a/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/background/ForkingInvocationHandler.java
+++ b/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/background/ForkingInvocationHandler.java
@@ -31,10 +31,13 @@ import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
import org.apache.isis.core.runtime.system.transaction.IsisTransaction;
import org.apache.isis.core.security.authentication.AuthenticationSession;
+import lombok.extern.slf4j.Slf4j;
+
/**
* Package private invocation handler that executes actions in the background using a ExecutorService
* @since 2.0.0
*/
+@Slf4j
class ForkingInvocationHandler<T> implements InvocationHandler {
private final T target;
@@ -90,8 +93,7 @@ class ForkingInvocationHandler<T> implements InvocationHandler {
authSession );
} catch (Exception e) {
- // log in caller's context
- BackgroundServiceDefault.LOG.error(
+ log.error(
String.format("Background execution of action '%s' on object '%s' failed.",
proxyMethod.getName(),
domainObject.getClass().getName()),
diff --git a/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/factory/FactoryServiceInternalDefault.java b/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/factory/FactoryServiceInternalDefault.java
index 3d4ca78..31f9ec3 100644
--- a/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/factory/FactoryServiceInternalDefault.java
+++ b/core/runtime-services/src/main/java/org/apache/isis/core/runtime/services/factory/FactoryServiceInternalDefault.java
@@ -53,12 +53,6 @@ public class FactoryServiceInternalDefault implements FactoryService {
return objectAdapterProvider.newTransientInstance(spec);
}
-
- @Override
- public <T> T m(final Class<T> mixinClass, final Object mixedIn) {
- return mixin(mixinClass, mixedIn);
- }
-
@Override
public <T> T mixin(final Class<T> mixinClass, final Object mixedIn) {
final ObjectSpecification objectSpec = specificationLoader.loadSpecification(mixinClass);
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/memento/Memento.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/memento/Memento.java
index 06da33d..ada069a 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/memento/Memento.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/memento/Memento.java
@@ -34,6 +34,7 @@ import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
import org.apache.isis.core.metamodel.facets.properties.update.modify.PropertySetterFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecId;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.feature.Contributed;
import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
@@ -193,7 +194,7 @@ public class Memento implements Serializable {
return null;
}
final ObjectSpecification spec =
- getSpecificationLoader().loadSpecification(data.getClassName());
+ getSpecificationLoader().loadSpecification(ObjectSpecId.of(data.getClassName()));
final Oid oid = getOid();
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/memento/StandaloneData.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/memento/StandaloneData.java
index 150ac7d..1f89b19 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/memento/StandaloneData.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/memento/StandaloneData.java
@@ -28,6 +28,7 @@ import org.apache.isis.core.commons.encoding.DataInputExtended;
import org.apache.isis.core.commons.encoding.DataOutputExtended;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecId;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.runtime.system.context.IsisContext;
@@ -120,7 +121,8 @@ public class StandaloneData extends Data {
if (objectAsSerializable != null) {
return IsisContext.pojoToAdapter().apply(objectAsSerializable);
} else {
- final ObjectSpecification spec = getSpecificationLoader().loadSpecification(getClassName());
+ final ObjectSpecification spec =
+ getSpecificationLoader().loadSpecification(ObjectSpecId.of(getClassName()));
final EncodableFacet encodeableFacet = spec.getFacet(EncodableFacet.class);
return encodeableFacet.fromEncodedString(objectAsEncodedString);
}
diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/CollectionMemento.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/CollectionMemento.java
index a1bc24b..54aae5d 100644
--- a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/CollectionMemento.java
+++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/CollectionMemento.java
@@ -30,8 +30,8 @@ import org.apache.isis.core.runtime.system.context.IsisContext;
import lombok.val;
/**
- * {@link Serializable} representation of a {@link OneToManyAssociation} (a
- * parented collection of entities).
+ * {@link Serializable} representation of a {@link OneToManyAssociation}
+ * (a parented collection of entities).
*/
public class CollectionMemento implements Serializable {
@@ -41,7 +41,8 @@ public class CollectionMemento implements Serializable {
final OneToManyAssociation association) {
val specificationLoader = IsisContext.getSpecificationLoader();
- return specificationLoader.loadSpecification(association.getIdentifier().toClassIdentityString());
+ return specificationLoader.loadSpecification(
+ ObjectSpecId.of(association.getIdentifier().toClassIdentityString()));
}
private final ObjectSpecId owningType;
diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/PropertyMemento.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/PropertyMemento.java
index 47cb086..f9dbc80 100644
--- a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/PropertyMemento.java
+++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/PropertyMemento.java
@@ -37,7 +37,8 @@ public class PropertyMemento implements Serializable {
private static ObjectSpecification owningSpecFor(
final OneToOneAssociation property) {
val specificationLoader = IsisContext.getSpecificationLoader();
- return specificationLoader.loadSpecification(property.getIdentifier().toClassIdentityString());
+ return specificationLoader.loadSpecification(
+ ObjectSpecId.of(property.getIdentifier().toClassIdentityString()));
}
private final ObjectSpecId owningSpecId;
diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/SpecUtils.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/SpecUtils.java
index e2165b4..b9742da 100644
--- a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/SpecUtils.java
+++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/mementos/SpecUtils.java
@@ -22,6 +22,7 @@ import org.apache.isis.core.metamodel.spec.ObjectSpecId;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+@Deprecated //TODO [2033] adds no value
public final class SpecUtils {
private SpecUtils(){}
@@ -29,13 +30,8 @@ public final class SpecUtils {
public static ObjectSpecification getSpecificationFor(
final ObjectSpecId objectSpecId,
final SpecificationLoader specificationLoader) {
- ObjectSpecification objectSpec = specificationLoader.lookupBySpecId(objectSpecId);
- if(objectSpec != null) {
- return objectSpec;
- }
-
- // attempt to load directly.
- return specificationLoader.loadSpecification(objectSpecId.asString());
+
+ return specificationLoader.lookupBySpecId(objectSpecId);
}
diff --git a/example/application/springapp/src/main/java/isis/incubator/IsisBoot.java b/example/application/springapp/src/main/java/isis/incubator/IsisBoot.java
index 7d2b796..3cee4e4 100644
--- a/example/application/springapp/src/main/java/isis/incubator/IsisBoot.java
+++ b/example/application/springapp/src/main/java/isis/incubator/IsisBoot.java
@@ -28,6 +28,7 @@ import springapp.boot.web.SpringAppManifest;
@Configuration
@ComponentScan(
basePackageClasses= {
+ IsisIncubatorModule.class,
IsisApplibModule.class,
MetamodelModule.class,
RuntimeModule.class,
diff --git a/example/application/springapp/src/main/java/isis/incubator/IsisIncubatorModule.java b/example/application/springapp/src/main/java/isis/incubator/IsisIncubatorModule.java
new file mode 100644
index 0000000..aaa7108
--- /dev/null
+++ b/example/application/springapp/src/main/java/isis/incubator/IsisIncubatorModule.java
@@ -0,0 +1,5 @@
+package isis.incubator;
+
+final class IsisIncubatorModule {
+
+}
diff --git a/example/application/springapp/src/main/java/isis/incubator/command/CommandSchedulerServiceInMemory.java b/example/application/springapp/src/main/java/isis/incubator/command/CommandSchedulerServiceInMemory.java
new file mode 100644
index 0000000..35132f2
--- /dev/null
+++ b/example/application/springapp/src/main/java/isis/incubator/command/CommandSchedulerServiceInMemory.java
@@ -0,0 +1,23 @@
+package isis.incubator.command;
+
+import org.apache.isis.applib.services.background.CommandSchedulerService;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.schema.cmd.v1.CommandDto;
+import org.springframework.stereotype.Service;
+
+@Service
+public class CommandSchedulerServiceInMemory implements CommandSchedulerService {
+
+ @Override
+ public void schedule(
+ CommandDto dto,
+ Command parentCommand,
+ String targetClassName,
+ String targetActionName,
+ String targetArgs) {
+
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/example/application/springapp/src/main/java/isis/incubator/command/IncubatingBackgroundExecutionService.java b/example/application/springapp/src/main/java/isis/incubator/command/IncubatingBackgroundExecutionService.java
new file mode 100644
index 0000000..3ae2c05
--- /dev/null
+++ b/example/application/springapp/src/main/java/isis/incubator/command/IncubatingBackgroundExecutionService.java
@@ -0,0 +1,92 @@
+package isis.incubator.command;
+
+import static org.apache.isis.commons.internal.base._Casts.uncheckedCast;
+
+import java.lang.reflect.InvocationHandler;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.services.background.BackgroundExecutionService;
+import org.apache.isis.applib.services.background.CommandSchedulerService;
+import org.apache.isis.applib.services.command.CommandContext;
+import org.apache.isis.applib.services.factory.FactoryService;
+import org.apache.isis.commons.internal._Constants;
+import org.apache.isis.core.commons.lang.ArrayExtensions;
+import org.apache.isis.core.metamodel.services.command.CommandDtoServiceInternal;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.metamodel.specloader.classsubstitutor.ProxyEnhanced;
+import org.apache.isis.core.plugins.codegen.ProxyFactory;
+import org.springframework.stereotype.Service;
+
+@Service
+public class IncubatingBackgroundExecutionService implements BackgroundExecutionService {
+
+ @Override
+ public <T> T execute(final T domainObject) {
+ final Class<T> cls = uncheckedCast(domainObject.getClass());
+ final InvocationHandler methodHandler = newMethodHandler(domainObject, null);
+ return newProxy(cls, null, methodHandler);
+ }
+
+ @Override
+ public <T> T executeMixin(Class<T> mixinClass, Object mixedIn) {
+ final T mixin = factoryService.mixin(mixinClass, mixedIn);
+ final InvocationHandler methodHandler = newMethodHandler(mixin, mixedIn);
+ return newProxy(mixinClass, mixedIn, methodHandler);
+ }
+
+ private <T> T newProxy(
+ final Class<T> cls,
+ final Object mixedInIfAny,
+ final InvocationHandler methodHandler) {
+
+ final Class<?>[] interfaces = ArrayExtensions.combine(
+ cls.getInterfaces(),
+ new Class<?>[] { ProxyEnhanced.class });
+
+ final boolean initialize = mixedInIfAny!=null;
+
+
+ final Class<?>[] constructorArgTypes = initialize ? new Class<?>[] {mixedInIfAny.getClass()} : _Constants.emptyClasses;
+ final Object[] constructorArgs = initialize ? new Object[] {mixedInIfAny} : _Constants.emptyObjects;
+
+ final ProxyFactory<T> proxyFactory = ProxyFactory.builder(cls)
+ .interfaces(interfaces)
+ .constructorArgTypes(constructorArgTypes)
+ .build();
+
+ return initialize
+ ? proxyFactory.createInstance(methodHandler, constructorArgs)
+ : proxyFactory.createInstance(methodHandler, false)
+ ;
+ }
+
+ /**
+ *
+ * @param target - the object that is proxied, either a domain object or a mixin around a domain object
+ * @param mixedInIfAny - if target is a mixin, then this is the domain object that is mixed-in to.
+ */
+ private <T> InvocationHandler newMethodHandler(final T target, final Object mixedInIfAny) {
+
+ return IncubatingCommandInvocationHandler.builder()
+ .commandSchedulerService(commandSchedulerService)
+ .target(target)
+ .mixedInIfAny(mixedInIfAny)
+ .specificationLoader(specificationLoader)
+ .commandDtoServiceInternal(commandDtoServiceInternal)
+ .toplevelCommandSupplier(new CommandContext())
+ .build();
+
+ }
+
+
+ // //////////////////////////////////////
+
+ //@Inject @Any private Instance<BackgroundCommandService> backgroundCommandServices;
+ @Inject private CommandSchedulerService commandSchedulerService;
+ @Inject private CommandDtoServiceInternal commandDtoServiceInternal;
+ //@Inject private CommandContext commandContext;
+ @Inject private FactoryService factoryService;
+ @Inject private SpecificationLoader specificationLoader;
+
+}
diff --git a/example/application/springapp/src/main/java/isis/incubator/command/IncubatingCommandDtoServiceInternal.java b/example/application/springapp/src/main/java/isis/incubator/command/IncubatingCommandDtoServiceInternal.java
new file mode 100644
index 0000000..fb5a0cf
--- /dev/null
+++ b/example/application/springapp/src/main/java/isis/incubator/command/IncubatingCommandDtoServiceInternal.java
@@ -0,0 +1,56 @@
+package isis.incubator.command;
+
+import java.util.List;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.services.command.CommandDtoServiceInternal;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.schema.cmd.v1.ActionDto;
+import org.apache.isis.schema.cmd.v1.CommandDto;
+import org.apache.isis.schema.cmd.v1.PropertyDto;
+import org.springframework.stereotype.Service;
+
+@Service
+public class IncubatingCommandDtoServiceInternal implements CommandDtoServiceInternal {
+
+ @Override
+ public CommandDto asCommandDto(
+ List<ObjectAdapter> targetAdapters,
+ ObjectAction objectAction,
+ ObjectAdapter[] argAdapters) {
+
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public CommandDto asCommandDto(
+ List<ObjectAdapter> targetAdapters,
+ OneToOneAssociation association,
+ ObjectAdapter valueAdapterOrNull) {
+
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void addActionArgs(
+ ObjectAction objectAction,
+ ActionDto actionDto,
+ ObjectAdapter[] argAdapters) {
+
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void addPropertyValue(
+ OneToOneAssociation property,
+ PropertyDto propertyDto,
+ ObjectAdapter valueAdapter) {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/example/application/springapp/src/main/java/isis/incubator/command/IncubatingCommandInvocationHandler.java b/example/application/springapp/src/main/java/isis/incubator/command/IncubatingCommandInvocationHandler.java
new file mode 100644
index 0000000..bd9c084
--- /dev/null
+++ b/example/application/springapp/src/main/java/isis/incubator/command/IncubatingCommandInvocationHandler.java
@@ -0,0 +1,33 @@
+package isis.incubator.command;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.function.Supplier;
+
+import org.apache.isis.applib.services.background.CommandSchedulerService;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapterProvider;
+import org.apache.isis.core.metamodel.services.command.CommandDtoServiceInternal;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+
+import lombok.Builder;
+import lombok.NonNull;
+
+@Builder
+class IncubatingCommandInvocationHandler<T> implements InvocationHandler {
+
+ @NonNull private final CommandSchedulerService commandSchedulerService;
+ @NonNull private final T target;
+ private final Object mixedInIfAny;
+ @NonNull private final SpecificationLoader specificationLoader;
+ @NonNull private final CommandDtoServiceInternal commandDtoServiceInternal;
+ @NonNull private final Supplier<Command> toplevelCommandSupplier;
+ @NonNull private final Supplier<ObjectAdapterProvider> adapterProviderSupplier;
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/example/application/springapp/src/main/java/isis/incubator/command/IncubatingFactoryService.java b/example/application/springapp/src/main/java/isis/incubator/command/IncubatingFactoryService.java
new file mode 100644
index 0000000..1ddbb66
--- /dev/null
+++ b/example/application/springapp/src/main/java/isis/incubator/command/IncubatingFactoryService.java
@@ -0,0 +1,21 @@
+package isis.incubator.command;
+
+import org.apache.isis.applib.services.factory.FactoryService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class IncubatingFactoryService implements FactoryService {
+
+ @Override
+ public <T> T instantiate(Class<T> domainClass) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <T> T mixin(Class<T> mixinClass, Object mixedIn) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/example/application/springapp/src/main/java/springapp/dom/customer/Customer.java b/example/application/springapp/src/main/java/springapp/dom/customer/Customer.java
index 1f0c9f1..11c41d8 100644
--- a/example/application/springapp/src/main/java/springapp/dom/customer/Customer.java
+++ b/example/application/springapp/src/main/java/springapp/dom/customer/Customer.java
@@ -1,32 +1,39 @@
package springapp.dom.customer;
+import java.util.List;
+
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
+import javax.persistence.OneToMany;
import org.apache.isis.applib.annotation.DomainObject;
import org.apache.isis.applib.annotation.Nature;
+import lombok.AccessLevel;
import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
import lombok.ToString;
+import springapp.dom.email.Email;
@DomainObject(nature=Nature.EXTERNAL_ENTITY)
-@Entity @ToString
+@Entity
+@NoArgsConstructor(access = AccessLevel.PROTECTED) @RequiredArgsConstructor @ToString
public class Customer {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Getter private Long id;
- @Getter private String firstName;
- @Getter private String lastName;
-
- protected Customer() {}
-
- public Customer(String firstName, String lastName) {
- this.firstName = firstName;
- this.lastName = lastName;
- }
+ @Getter @NonNull private String firstName;
+ @Getter @NonNull private String lastName;
+
+ @OneToMany(mappedBy = "customer")
+ @Getter @Setter
+ private List<Email> emails;
}
diff --git a/example/application/springapp/src/main/java/springapp/dom/customer/CustomerRepository.java b/example/application/springapp/src/main/java/springapp/dom/customer/CustomerRepository.java
index 362e069..e40c9c2 100644
--- a/example/application/springapp/src/main/java/springapp/dom/customer/CustomerRepository.java
+++ b/example/application/springapp/src/main/java/springapp/dom/customer/CustomerRepository.java
@@ -5,5 +5,7 @@ import java.util.List;
import org.springframework.data.repository.CrudRepository;
public interface CustomerRepository extends CrudRepository<Customer, Long> {
+
List<Customer> findByLastName(String lastName);
+
}
diff --git a/example/application/springapp/src/main/java/springapp/dom/email/Email.java b/example/application/springapp/src/main/java/springapp/dom/email/Email.java
new file mode 100644
index 0000000..cb7f4d9
--- /dev/null
+++ b/example/application/springapp/src/main/java/springapp/dom/email/Email.java
@@ -0,0 +1,49 @@
+package springapp.dom.email;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Nature;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import springapp.dom.customer.Customer;
+
+@DomainObject(nature=Nature.EXTERNAL_ENTITY)
+@Entity
+@NoArgsConstructor(access = AccessLevel.PROTECTED) @RequiredArgsConstructor @ToString
+public class Email {
+
+ @Id
+ @GeneratedValue(strategy=GenerationType.AUTO)
+ @Getter
+ private Long id;
+
+ @ManyToOne
+ @Getter @Setter @NonNull
+ private Customer customer;
+
+ @Getter @Setter @NonNull
+ private String address;
+
+ @Getter @Setter
+ private boolean verified;
+
+ // -- BUSINESS LOGIC
+
+ public void startVerificationProcess() {
+
+ System.out.println("startVerificationProcess for " + this);
+ }
+
+
+}
diff --git a/example/application/springapp/src/main/java/springapp/dom/email/EmailRepository.java b/example/application/springapp/src/main/java/springapp/dom/email/EmailRepository.java
new file mode 100644
index 0000000..4aee42e
--- /dev/null
+++ b/example/application/springapp/src/main/java/springapp/dom/email/EmailRepository.java
@@ -0,0 +1,11 @@
+package springapp.dom.email;
+
+import java.util.List;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface EmailRepository extends CrudRepository<Email, Long> {
+
+ List<Email> findByVerified(boolean verified);
+
+}
diff --git a/example/application/springapp/src/test/java/springapp/tests/command/CommandDemoBean.java b/example/application/springapp/src/test/java/springapp/tests/command/CommandDemoBean.java
new file mode 100644
index 0000000..a4521d2
--- /dev/null
+++ b/example/application/springapp/src/test/java/springapp/tests/command/CommandDemoBean.java
@@ -0,0 +1,29 @@
+package springapp.tests.command;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.services.background.BackgroundExecutionService;
+import org.springframework.stereotype.Service;
+
+import springapp.dom.customer.CustomerRepository;
+import springapp.dom.email.Email;
+import springapp.dom.email.EmailRepository;
+
+@Service
+public class CommandDemoBean {
+
+ @Inject EmailRepository emailRepository;
+ @Inject CustomerRepository customerRepository;
+ @Inject BackgroundExecutionService backgroundService;
+
+ @Action
+ public void verifyCustomerEmails() {
+
+ for(Email email: emailRepository.findByVerified(false)) {
+ backgroundService.execute(email).startVerificationProcess();
+ }
+
+ }
+
+}
diff --git a/example/application/springapp/src/test/java/springapp/tests/command/CommandTest.java b/example/application/springapp/src/test/java/springapp/tests/command/CommandTest.java
new file mode 100644
index 0000000..99c3c1c
--- /dev/null
+++ b/example/application/springapp/src/test/java/springapp/tests/command/CommandTest.java
@@ -0,0 +1,30 @@
+package springapp.tests.command;
+
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import springapp.boot.test.SpringBootTestApplication;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(classes = {SpringBootTestApplication.class, CommandDemoBean.class})
+@AutoConfigureTestDatabase
+@EnableAsync
+@TestMethodOrder(OrderAnnotation.class)
+class CommandTest {
+
+ //@Inject AsyncExecutionService asyncExecutionService;
+
+ @Test
+ void shouldAllowTaskCancellation() {
+
+
+ }
+
+
+}
diff --git a/example/application/springapp/src/test/java/springapp/tests/command/MessageBox.java b/example/application/springapp/src/test/java/springapp/tests/command/MessageBox.java
new file mode 100644
index 0000000..480d658
--- /dev/null
+++ b/example/application/springapp/src/test/java/springapp/tests/command/MessageBox.java
@@ -0,0 +1,27 @@
+package springapp.tests.command;
+
+import java.util.Collections;
+import java.util.stream.Stream;
+
+import org.apache.isis.commons.internal.collections._Multimaps;
+import org.springframework.stereotype.Service;
+
+import lombok.val;
+
+@Service
+public class MessageBox {
+
+ private final _Multimaps.SetMultimap<String, String> store = _Multimaps.newSetMultimap();
+
+ public void send(String key, String msg) {
+ synchronized (store) {
+ store.putElement(key, msg);
+ }
+ }
+
+ public Stream<String> streamMessages(String key) {
+ val messages = store.getOrDefault(key, Collections.emptySet());
+ return messages.stream();
+ }
+
+}