You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2020/09/20 10:48:41 UTC
[isis] branch ISIS-2222 updated: ISIS-2222: reworking Command and
other stuff
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch ISIS-2222
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/ISIS-2222 by this push:
new c2bd0db ISIS-2222: reworking Command and other stuff
c2bd0db is described below
commit c2bd0db9b4d345fd10656aee4aa57b1084721128
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Sat Sep 19 18:00:06 2020 +0100
ISIS-2222: reworking Command and other stuff
---
.../applib-ant/examples/annotation/Action.java | 4 +-
.../applib-ant/examples/annotation/Property.java | 4 +-
.../events/domain/AbstractDomainEvent.java | 3 +-
.../examples/util/schema/CommandDtoUtils.java | 13 +-
.../examples/services/clock/ClockService.java | 11 +-
.../examples/services/command/Command.java | 178 ++++----
.../examples/services/command/CommandContext.java | 81 ----
.../services/command/CommandExecutorService.java | 16 +-
.../services/command/CommandOutcomeHandler.java | 24 +
.../examples/services/command/CommandService.java | 24 +-
.../services/commanddto/HasCommandDto.java | 12 +
.../conmap/ContentMappingServiceForCommandDto.java | 106 +++++
.../ContentMappingServiceForCommandsDto.java | 2 +-
.../conmap}/UserDataKeys.java | 2 +-
.../processor}/CommandDtoProcessor.java | 33 +-
.../CommandDtoProcessorForActionAbstract.java | 6 +-
.../CommandDtoProcessorForPropertyAbstract.java | 6 +-
.../processor}/spi/CommandDtoProcessorService.java | 24 +-
.../spi/CommandDtoProcessorServiceIdentity.java | 23 +
.../ContentMappingServiceForCommandDto.java | 153 -------
.../examples/services/iactn/Interaction.java | 152 ++++---
.../services/iactn/InteractionContext.java | 33 +-
.../services/metamodel/MetaModelService.java | 4 +-
.../TableColumnOrderForCollectionTypeAbstract.java | 4 +-
.../services/wrapper/control/AsyncControl.java | 18 +-
.../wrapper/control/AsyncControlService.java | 52 ---
.../services/wrapper/control/ControlAbstract.java | 10 +-
.../org/apache/isis/applib/IsisModuleApplib.java | 2 +
.../command/spi/CommandServiceListener.java | 27 ++
.../services/wrapper/control/AsyncControl.java | 15 +-
.../services/wrapper/control/ControlAbstract.java | 4 +-
.../isis/applib/util/schema/CommandDtoUtils.java | 8 +
.../wrapper/control/AsyncControl_Test.java | 13 +-
.../applib/util/schema/CommandDtoUtils_Test.java | 13 +-
.../ActionAnnotationFacetFactoryTest_Command.java | 49 +-
.../DomainObjectLayoutFactoryTest.java | 5 +-
.../ImageValueSemanticsProviderAbstractTest.java | 4 -
.../command/CommandExecutorServiceDefault.java | 10 +-
.../wrapper/WrapperFactoryDefault.java | 79 ++--
.../WrapperFactoryDefault_wrappedObject_Test.java | 503 ---------------------
...actoryDefault_wrappedObject_transient_Test.java | 299 ------------
.../Action/command/ActionCommandJdo.java | 61 +--
.../Action/command/ActionCommandJdo.layout.xml | 27 +-
.../ActionCommandJdo_mixinUpdateProperty.java | 5 +-
...andJdo_mixinUpdatePropertyCommandDisabled.java} | 12 +-
...mmandJdo_mixinUpdatePropertyMetaAnnotation.java | 10 +-
...ixinUpdatePropertyMetaAnnotationOverridden.java | 9 +-
.../demoapp/dom/events/DemoEventSubscriber.java | 2 +-
.../src/main/java/demoapp/dom/menubars.layout.xml | 13 +-
.../java/demoapp/dom/services/ServicesMenu.java | 48 ++
.../WrapperFactoryJdo-description.adoc | 2 +
.../services/wrapperFactory/WrapperFactoryJdo.java | 127 ++++++
.../wrapperFactory/WrapperFactoryJdo.layout.xml} | 30 +-
.../wrapperFactory/WrapperFactoryJdoEntities.java | 33 ++
.../WrapperFactoryJdoSeedService.java | 39 ++
...WrapperFactoryJdo_mixinUpdatePropertyAsync.java | 44 ++
...WrapperFactoryJdo_updatePropertyAsyncMixin.java | 40 ++
.../testdomain/conf/Configuration_headless.java | 14 +-
.../impl/CommandServiceListenerForJdo.java | 39 +-
.../commandlog/impl/jdo/CommandJdoRepository.java | 23 +-
.../commandlog/impl/ui/CommandServiceMenu.java | 17 +-
.../secondary/fetch/CommandFetcher_Test.java | 2 +
.../ui/auth/VaadinAuthenticationHandler.java | 2 +-
.../services/eventbus/ActionDomainEvent.java | 6 +-
.../adoc/modules/integtestsupport/pages/about.adoc | 2 +-
.../applib/IsisIntegrationTestAbstract.java | 13 +-
.../applib/IsisInteractionHandler.java | 2 +-
tooling/javamodel/pom.xml | 1 +
tooling/projectmodel/pom.xml | 1 +
.../isis/viewer/wicket/ui/errors/JGrowlUtil.java | 4 +-
.../wicket/ui/panels/FormExecutorDefault.java | 9 +-
71 files changed, 1123 insertions(+), 1543 deletions(-)
diff --git a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Action.java b/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Action.java
index 89bbaa5..f8c0703 100644
--- a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Action.java
+++ b/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Action.java
@@ -28,8 +28,8 @@ import java.lang.annotation.Target;
import org.apache.isis.applib.events.domain.ActionDomainEvent;
import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
import org.apache.isis.applib.services.command.CommandService;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandDto;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandsDto;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandsDto;
import org.apache.isis.applib.value.Blob;
import org.apache.isis.applib.value.Clob;
diff --git a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Property.java b/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Property.java
index 0600ead..65db374 100644
--- a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Property.java
+++ b/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Property.java
@@ -27,8 +27,8 @@ import java.lang.annotation.Target;
import org.apache.isis.applib.events.domain.PropertyDomainEvent;
import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandDto;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandsDto;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandsDto;
import org.apache.isis.applib.spec.Specification;
import org.apache.isis.applib.value.Blob;
import org.apache.isis.applib.value.Clob;
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/AbstractDomainEvent.java b/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/AbstractDomainEvent.java
index a61b5ae..9e6d244 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/AbstractDomainEvent.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/AbstractDomainEvent.java
@@ -22,7 +22,6 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.isis.applib.Identifier;
-import org.apache.isis.applib.annotation.Programmatic;
import org.apache.isis.applib.events.EventObjectBase;
import org.apache.isis.applib.services.i18n.TranslatableString;
import org.apache.isis.applib.util.ObjectContracts;
@@ -107,7 +106,7 @@ public abstract class AbstractDomainEvent<S> extends EventObjectBase<S> {
/**
* When the {@link org.apache.isis.applib.services.command.Command} is made available on the
- * {@link org.apache.isis.applib.events.domain.ActionDomainEvent} via {@link Interaction#getCommand()}.
+ * {@link org.apache.isis.applib.events.domain.ActionDomainEvent} via {@link org.apache.isis.applib.services.iactn.Interaction#getCommand()}.
*/
public boolean isExecutingOrLater() {
return isExecuting() || isExecuted();
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/util/schema/CommandDtoUtils.java b/api/applib/src/main/adoc/modules/applib-classes/examples/util/schema/CommandDtoUtils.java
index 9b6c93b0..75512e5 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/util/schema/CommandDtoUtils.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/util/schema/CommandDtoUtils.java
@@ -30,7 +30,10 @@ import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
+import org.apache.isis.applib.services.bookmark.Bookmark;
import org.apache.isis.applib.util.JaxbUtil;
+import org.apache.isis.core.commons.internal.base._Strings;
+import org.apache.isis.core.commons.internal.exceptions._Exceptions;
import org.apache.isis.core.commons.internal.resources._Resources;
import org.apache.isis.schema.cmd.v2.ActionDto;
import org.apache.isis.schema.cmd.v2.CommandDto;
@@ -134,13 +137,21 @@ public final class CommandDtoUtils {
public static void setUserData(
final CommandDto dto, final String key, final String value) {
- if(dto == null || key == null) {
+ if(dto == null || key == null || _Strings.isNullOrEmpty(value)) {
return;
}
final MapDto userData = userDataFor(dto);
CommonDtoUtils.putMapKeyValue(userData, key, value);
}
+ public static void setUserData(
+ final CommandDto dto, final String key, final Bookmark bookmark) {
+ if(dto == null || key == null || bookmark == null) {
+ return;
+ }
+ setUserData(dto, key, bookmark.toString());
+ }
+
private static MapDto userDataFor(final CommandDto commandDto) {
MapDto userData = commandDto.getUserData();
if(userData == null) {
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/clock/ClockService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/clock/ClockService.java
index 597a56b..4583a4c 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/clock/ClockService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/clock/ClockService.java
@@ -18,15 +18,11 @@
*/
package org.apache.isis.applib.services.clock;
-import java.sql.Timestamp;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.OffsetDateTime;
import java.util.TimeZone;
import javax.inject.Named;
+import javax.xml.datatype.XMLGregorianCalendar;
-import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
@@ -35,6 +31,7 @@ import org.springframework.stereotype.Service;
import org.apache.isis.applib.annotation.OrderPrecedence;
import org.apache.isis.applib.clock.Clock;
+import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
/**
* This service allows an application to be decoupled from the system time. The most common use case is in support of
@@ -66,6 +63,10 @@ public class ClockService {
return Clock.getTimeAsJavaSqlTimestamp();
}
+ public XMLGregorianCalendar nowAsXMLGregorianCalendar() {
+ return JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(nowAsJavaSqlTimestamp());
+ }
+
public long nowAsMillis() {
return Clock.getEpochMillis();
}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/Command.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/Command.java
index 62df89d..dc55a40 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/Command.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/Command.java
@@ -22,16 +22,16 @@ import java.sql.Timestamp;
import java.util.UUID;
import org.apache.isis.applib.events.domain.ActionDomainEvent;
+import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
import org.apache.isis.applib.services.HasUniqueId;
import org.apache.isis.applib.services.HasUsername;
import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.commanddto.HasCommandDto;
import org.apache.isis.applib.services.iactn.Interaction;
import org.apache.isis.applib.services.wrapper.WrapperFactory;
import org.apache.isis.applib.services.wrapper.control.AsyncControl;
import org.apache.isis.schema.cmd.v2.CommandDto;
-import static org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling.toTimestamp;
-
import lombok.Getter;
/**
@@ -67,46 +67,70 @@ import lombok.Getter;
* </p>
*/
// tag::refguide[]
-public class Command implements HasUniqueId, HasUsername {
-
- public Command() {
- this(UUID.randomUUID());
- }
- public Command(final Command parent, final CommandDto commandDto) {
- this(UUID.fromString(commandDto.getTransactionId()));
- outcomeHandler().setCommandDto(commandDto);
- outcomeHandler().setParent(parent);
- outcomeHandler().setUsername(commandDto.getUser());
- outcomeHandler().setTimestamp(toTimestamp(commandDto.getTimestamp()));
- }
-
- private Command(final UUID uuid) {
- this.uniqueId = uuid;
- }
-
-
- @Getter
- private UUID uniqueId;
+public class Command implements HasUniqueId, HasUsername, HasCommandDto {
// end::refguide[]
/**
+ * Unique identifier for the command.
+ *
+ * <p>
+ * Derived from {@link #getCommandDto()}'s {@link CommandDto#getTransactionId()}
+ * </p>
+ */
+ @Override
+ // tag::refguide[]
+ public UUID getUniqueId() { // <.>
+ // ...
+ // end::refguide[]
+ return commandDto != null
+ ? UUID.fromString(commandDto.getTransactionId())
+ : null;
+ }
+ /**
* The user that created the command.
+ *
+ * <p>
+ * Derived from {@link #getCommandDto()}'s {@link CommandDto#getUser()}
+ * </p>
*/
+ @Override
// tag::refguide[]
- @Getter
- private String username; // <.>
- // end::refguide[]
+ public String getUsername() { // <.>
+ // ...
+ // end::refguide[]
+ return commandDto != null
+ ? commandDto.getUser()
+ : null;
+ }
/**
* The date/time at which this command was created.
+ *
+ * <p>
+ * Derived from {@link #getCommandDto()}'s {@link CommandDto#getTimestamp()}.
+ * </p>
*/
// tag::refguide[]
- @Getter
- private Timestamp timestamp; // <.>
- // end::refguide[]
+ public Timestamp getTimestamp() { // <.>
+ // ...
+ // end::refguide[]
+ return commandDto != null
+ ? JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(commandDto.getTimestamp())
+ : null;
+ }
/**
* Serializable representation of the action invocation/property edit.
+ *
+ * <p>
+ * When the framework sets this (through an internal API), it is
+ * expected to have {@link CommandDto#getTransactionId()},
+ * {@link CommandDto#getUser()}, {@link CommandDto#getTimestamp()},
+ * {@link CommandDto#getTargets()} and {@link CommandDto#getMember()}
+ * to be populated. The {@link #getUniqueId()}, {@link #getUsername()},
+ * {@link #getTimestamp()} and {@link #getTarget()} are all derived
+ * from the provided {@link CommandDto}.
+ * </p>
*/
// tag::refguide[]
@Getter
@@ -114,32 +138,40 @@ public class Command implements HasUniqueId, HasUsername {
// end::refguide[]
/**
- * Also available in {@link #getCommandDto()}, is the {@link Bookmark} of
+ * Derived from {@link #getCommandDto()}, is the {@link Bookmark} of
* the target object (entity or service) on which this action/edit was performed.
*/
// tag::refguide[]
- @Getter
- private Bookmark target; // <.>
+ public Bookmark getTarget() { // <.>
+ return commandDto != null
+ ? Bookmark.from(commandDto.getTargets().getOid().get(0))
+ : null;
+ }
// end::refguide[]
/**
- * Also available in {@link #getCommandDto()}, holds a string
+ * Derived from {@link #getCommandDto()}, holds a string
* representation of the invoked action, or the edited property.
*/
// tag::refguide[]
- @Getter
- private String logicalMemberIdentifier; // <.>
+ public String getLogicalMemberIdentifier() { // <.>
+ return commandDto != null
+ ? commandDto.getMember().getLogicalMemberIdentifier()
+ : null;
+ }
// end::refguide[]
/**
- * For commands created through the {@link WrapperFactory} (using
- * {@link WrapperFactory#asyncWrap(Object, AsyncControl)} or
- * {@link WrapperFactory#asyncWrapMixin(Class, Object, AsyncControl)}),
+ * For async commands created through the {@link WrapperFactory},
* captures the parent command.
*
* <p>
* Will return <code>null</code> if there is no parent.
* </p>
+ *
+ * @see WrapperFactory#asyncWrap(Object, AsyncControl)
+ * @see WrapperFactory#asyncWrapMixin(Class, Object, AsyncControl)
+ *
*/
// tag::refguide[]
@Getter
@@ -147,17 +179,11 @@ public class Command implements HasUniqueId, HasUsername {
// end::refguide[]
/**
- * For an command that has actually been executed, holds the date/time at which the {@link Interaction} that
- * executed the command started.
+ * For an command that has actually been executed, holds the date/time at
+ * which the {@link Interaction} that executed the command started.
*
- * <p>
- * Previously this field was deprecated (on the basis that the startedAt is also held in
- * {@link Interaction.Execution#getStartedAt()}). However, this property is now used in master/slave
- * replay scenarios which may query a persisted Command.
- * </p>
- *
- * See also {@link Interaction#getCurrentExecution()} and
- * {@link Interaction.Execution#getStartedAt()}.
+ * @see Interaction#getCurrentExecution()
+ * @see Interaction.Execution#getStartedAt()
*/
// tag::refguide[]
@Getter
@@ -234,26 +260,18 @@ public class Command implements HasUniqueId, HasUsername {
// end::refguide[]
- private final Command.Internal INTERNAL = new Internal();
+ private final Updater UPDATER = new Updater();
- public class Internal {
- /**
- * <b>NOT API</b>: intended to be called only by the framework.
- *
- * <p>
- * Implementation notes: set when the Isis PersistenceSession is opened.
- */
- public void setUsername(String username) {
- Command.this.username = username;
- }
+ public class Updater implements CommandOutcomeHandler {
/**
* <b>NOT API</b>: intended to be called only by the framework.
*
* <p>
- * Implementation notes: set when the Isis PersistenceSession is opened.
+ * Implementation notes: set when the action is invoked (in the <tt>ActionInvocationFacet</tt>).
+ * @param commandDto
*/
- public void setTimestamp(Timestamp timestamp) {
- Command.this.timestamp = timestamp;
+ public void setCommandDto(final CommandDto commandDto, final int targetIdx) {
+ Command.this.commandDto = commandDto;
}
/**
* <b>NOT API</b>: intended to be called only by the framework.
@@ -268,55 +286,37 @@ public class Command implements HasUniqueId, HasUsername {
}
/**
* <b>NOT API</b>: intended to be called only by the framework.
- *
- * <p>
- * Implementation notes: set when the action is invoked (in the <tt>ActionInvocationFacet</tt>).
- * @param commandDto
*/
- public void setCommandDto(CommandDto commandDto) {
- Command.this.commandDto = commandDto;
- }
- /**
- * <b>NOT API</b>: intended to be called only by the framework.
- *
- * <p>
- * Implementation notes: set when the action is invoked (in the ActionInvocationFacet).
- */
- public void setTarget(Bookmark target) {
- Command.this.target = target;
- }
- /**
- * <b>NOT API</b>: intended to be called only by the framework.
- *
- * <p>
- * Implementation notes: set when the action is invoked (in
- * <tt>ActionInvocationFacet</tt>) or property is edited (in
- * <tt>PropertySetterFacet</tt>).
- */
- public void setLogicalMemberIdentifier(String logicalMemberIdentifier) {
- Command.this.logicalMemberIdentifier = logicalMemberIdentifier;
+ @Override
+ public Timestamp getStartedAt() {
+ return Command.this.getStartedAt();
}
/**
* <b>NOT API</b>: intended to be called only by the framework.
*/
+ @Override
public void setStartedAt(Timestamp startedAt) {
Command.this.startedAt = startedAt;
}
/**
* <b>NOT API</b>: intended to be called only by the framework.
*/
+ @Override
public void setCompletedAt(final Timestamp completed) {
Command.this.completedAt = completed;
}
/**
* <b>NOT API</b>: intended to be called only by the framework.
*/
+ @Override
public void setResult(final Bookmark result) {
Command.this.result = result;
}
+
/**
* <b>NOT API</b>: intended to be called only by the framework.
*/
+ @Override
public void setException(final Throwable exception) {
Command.this.exception = exception;
}
@@ -336,8 +336,8 @@ public class Command implements HasUniqueId, HasUsername {
/**
* <b>NOT API</b>: intended to be called only by the framework.
*/
- public Command.Internal internal() {
- return INTERNAL;
+ public Updater updater() {
+ return UPDATER;
}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandContext.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandContext.java
deleted file mode 100644
index df73176..0000000
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandContext.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.isis.applib.services.command;
-
-import java.util.Optional;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.springframework.beans.factory.DisposableBean;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.context.annotation.Primary;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Service;
-
-import org.apache.isis.applib.annotation.IsisInteractionScope;
-import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.services.TransactionScopeListener;
-import org.apache.isis.applib.services.inject.ServiceInjector;
-import org.apache.isis.applib.services.metrics.MetricsService;
-import org.apache.isis.applib.services.registry.ServiceRegistry;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-/**
- * This service (API and implementation) provides access to context information about any {@link Command}.
- *
- * This implementation has no UI and there is only one implementation (this class) in applib, so it is annotated with
- * {@link org.apache.isis.applib.annotation.DomainService}. This means that it is automatically registered and
- * available for use; no further configuration is required.
- */
-// tag::refguide[]
-@Service
-@Named("isisApplib.CommandContext")
-@Order(OrderPrecedence.EARLY - 10) // before ChangedObjectService
-@Primary
-@Qualifier("Default")
-@IsisInteractionScope
-@RequiredArgsConstructor(onConstructor_ = {@Inject})
-//@Log4j2
-public class CommandContext implements DisposableBean {
-
- private final MetricsService metricsService;
-
- @Getter
- private Command command;
-
- // end::refguide[]
- /**
- * <b>NOT API</b>: intended to be called only by the framework.
- */
- public void setCommand(final Command command) {
- this.command = command;
- }
-
- @Override
- public void destroy() throws Exception {
- setCommand(null);
- }
-
-
- // tag::refguide[]
-}
-// end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandExecutorService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandExecutorService.java
index 4634dc5..4fce410 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandExecutorService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandExecutorService.java
@@ -53,22 +53,22 @@ public interface CommandExecutorService {
*/
// tag::refguide[]
Bookmark executeCommand(
- SudoPolicy sudoPolicy, // <.>
- Command command // <.>
+ SudoPolicy sudoPolicy, // <.>
+ Command command // <.>
);
Bookmark executeCommand(
- SudoPolicy sudoPolicy, // <.>
- CommandDto commandDto // <.>
- );
+ SudoPolicy sudoPolicy, // <.>
+ CommandDto commandDto, // <.>
+ CommandOutcomeHandler outcomeHandler); // <.>
Bookmark executeCommand(
- Command command // <.>
+ Command command // <.>
);
Bookmark executeCommand(
- CommandDto commandDto // <.>
- );
+ CommandDto commandDto, // <.>
+ CommandOutcomeHandler outcomeHandler); // <.>
}
// end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandOutcomeHandler.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandOutcomeHandler.java
new file mode 100644
index 0000000..ccd9530
--- /dev/null
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandOutcomeHandler.java
@@ -0,0 +1,24 @@
+package org.apache.isis.applib.services.command;
+
+import java.sql.Timestamp;
+
+import org.apache.isis.applib.services.bookmark.Bookmark;
+
+public interface CommandOutcomeHandler {
+
+ CommandOutcomeHandler NULL = new CommandOutcomeHandler() {
+ @Override public Timestamp getStartedAt() { return null; }
+ @Override public void setStartedAt(Timestamp startedAt) { }
+ @Override public void setCompletedAt(Timestamp completedAt) { }
+ @Override public void setResult(Bookmark resultBookmark) { }
+ @Override public void setException(Throwable throwable) { }
+ };
+
+ Timestamp getStartedAt();
+ void setStartedAt(Timestamp startedAt);
+
+ void setCompletedAt(Timestamp completedAt);
+
+ void setResult(Bookmark resultBookmark);
+ void setException(Throwable throwable);
+}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandService.java
index ce19893..eae10f0 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandService.java
@@ -18,6 +18,7 @@
*/
package org.apache.isis.applib.services.command;
+import java.sql.Timestamp;
import java.util.List;
import javax.inject.Inject;
@@ -30,9 +31,12 @@ import org.springframework.stereotype.Service;
import org.apache.isis.applib.annotation.IsisInteractionScope;
import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.services.clock.ClockService;
import org.apache.isis.applib.services.command.Command;
import org.apache.isis.applib.services.command.spi.CommandServiceListener;
+import org.apache.isis.applib.services.user.UserService;
+import lombok.val;
import lombok.extern.log4j.Log4j2;
@Service
@@ -46,22 +50,6 @@ public class CommandService {
// end::refguide[]
/**
- * Simply instantiates the appropriate instance of the {@link Command}.
- *
- * <p>
- * Its members will be populated automatically by the framework (the
- * {@link Command}'s {@link Command#getTimestamp()},
- * {@link Command#getUsername()} and {@link Command#getUniqueId()}).
- * </p>
- */
- // tag::refguide[]
- public Command create() { // <.>
- return new Command();
- }
-
- // end::refguide[]
-
- /**
* "Complete" the command, providing an opportunity ot persist
* a memento of the command if the
* {@link Command#isSystemStateChanged() system state has changed}.
@@ -80,7 +68,9 @@ public class CommandService {
return;
}
- log.debug("complete: {}, systemStateChanged {}", command.getLogicalMemberIdentifier(), command.isSystemStateChanged());
+ log.debug("complete: {}, systemStateChanged {}",
+ command.getLogicalMemberIdentifier(),
+ command.isSystemStateChanged());
// tag::refguide[]
commandServiceListeners.forEach(x -> x.onComplete(command));
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/HasCommandDto.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/HasCommandDto.java
new file mode 100644
index 0000000..6c9d631
--- /dev/null
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/HasCommandDto.java
@@ -0,0 +1,12 @@
+package org.apache.isis.applib.services.commanddto;
+
+import org.apache.isis.schema.cmd.v2.CommandDto;
+
+/**
+ * Objects implementing this interface will be processed automatically by
+ * {@link org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto}.
+ */
+public interface HasCommandDto {
+
+ CommandDto getCommandDto();
+}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/ContentMappingServiceForCommandDto.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/ContentMappingServiceForCommandDto.java
new file mode 100644
index 0000000..6e1edfb
--- /dev/null
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/ContentMappingServiceForCommandDto.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.isis.applib.services.commanddto.conmap;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.ws.rs.core.MediaType;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.services.commanddto.HasCommandDto;
+import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
+import org.apache.isis.applib.services.conmap.ContentMappingService;
+import org.apache.isis.applib.services.commanddto.processor.spi.CommandDtoProcessorService;
+import org.apache.isis.applib.services.metamodel.MetaModelService;
+import org.apache.isis.applib.util.schema.CommandDtoUtils;
+import org.apache.isis.core.commons.internal.exceptions._Exceptions;
+import org.apache.isis.schema.cmd.v2.CommandDto;
+import org.apache.isis.schema.common.v2.PeriodDto;
+
+import lombok.val;
+
+@Service
+@Named("isisApplib.ContentMappingServiceForCommandDto")
+@Order(OrderPrecedence.EARLY)
+@Primary
+@Qualifier("CommandDto")
+public class ContentMappingServiceForCommandDto implements ContentMappingService {
+
+ @Override
+ public Object map(Object object, final List<MediaType> acceptableMediaTypes) {
+ final boolean supported = Util.isSupported(CommandDto.class, acceptableMediaTypes);
+ if(!supported) {
+ return null;
+ }
+
+ return asProcessedDto(object);
+ }
+
+ CommandDto asProcessedDto(final Object object) {
+ val commandDto = asCommandDto(object);
+ return asProcessedDto(object, commandDto);
+ }
+
+ private CommandDto asCommandDto(Object object) {
+ if(object instanceof CommandDto) {
+ return (CommandDto) object;
+ }
+ if(object instanceof HasCommandDto) {
+ return ((HasCommandDto) object).getCommandDto();
+ }
+ return null;
+ }
+
+ private CommandDto asProcessedDto(final Object domainObject, CommandDto commandDto) {
+
+ // global processors
+ for (final CommandDtoProcessorService commandDtoProcessorService : commandDtoProcessorServices) {
+ commandDto = commandDtoProcessorService.process(domainObject, commandDto);
+ if(commandDto == null) {
+ // any processor could return null, effectively breaking the chain.
+ return null;
+ }
+ }
+
+ // specific processor for this specific member (action or property)
+ val logicalMemberId = commandDto.getMember().getLogicalMemberIdentifier();
+ final CommandDtoProcessor commandDtoProcessor =
+ metaModelService.commandDtoProcessorFor(logicalMemberId);
+ if (commandDtoProcessor == null) {
+ return commandDto;
+ }
+ return commandDtoProcessor.process(commandDto);
+ }
+
+
+ @Inject MetaModelService metaModelService;
+ @Inject List<CommandDtoProcessorService> commandDtoProcessorServices;
+
+}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/ContentMappingServiceForCommandsDto.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/ContentMappingServiceForCommandsDto.java
similarity index 98%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/ContentMappingServiceForCommandsDto.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/ContentMappingServiceForCommandsDto.java
index ade5070..4d491b2 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/ContentMappingServiceForCommandsDto.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/ContentMappingServiceForCommandsDto.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.isis.applib.services.conmap.command;
+package org.apache.isis.applib.services.commanddto.conmap;
import java.util.List;
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/UserDataKeys.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/UserDataKeys.java
similarity index 95%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/UserDataKeys.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/UserDataKeys.java
index bc1bb2f..4fc4e59 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/UserDataKeys.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/UserDataKeys.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.isis.applib.services.conmap.command;
+package org.apache.isis.applib.services.commanddto.conmap;
import org.apache.isis.schema.cmd.v2.CommandDto;
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessor.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessor.java
similarity index 57%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessor.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessor.java
index 60edc81..a0706ad 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessor.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessor.java
@@ -16,33 +16,42 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.isis.applib.services.command;
+package org.apache.isis.applib.services.commanddto.processor;
import org.apache.isis.schema.cmd.v2.CommandDto;
+/**
+ * Refine (or possibly ignore) a command when replicating from primary
+ * to secondary.
+ */
// tag::refguide[]
public interface CommandDtoProcessor {
// end::refguide[]
/**
- * Returning <tt>null</tt> means that the command's DTO is effectively excluded from any list.
- * If replicating from master to slave, this allows commands that can't be replicated to be ignored.
- * @param command
- * @param commandDto
+ * The implementation can if necessary refine or alter the
+ * {@link CommandDto} to be replicated from primary to secondary.
+ *
+ * <p>
+ * That said, the most common use case is to return <code>null</code>,
+ * which results in the command effectively being ignore.
+ * </p>
+ *
+ * @param commandDto - to be processed
+ * @return <tt>null</tt> means that the command's DTO is effectively
+ * excluded.
*/
// tag::refguide[]
- CommandDto process(final Command command, CommandDto commandDto); // <.>
+ CommandDto process(CommandDto commandDto); // <.>
// end::refguide[]
/**
- * Convenience implementation to simply indicate that no DTO should be returned for a command,
- * effectively ignoring it for replay purposes.
+ * Convenience implementation to simply indicate that no DTO should be
+ * returned for a command, effectively ignoring it for replication purposes.
*/
- public static class Null implements CommandDtoProcessor {
+ class Null implements CommandDtoProcessor {
@Override
- public CommandDto process(
- final Command command,
- final CommandDto commandDto) {
+ public CommandDto process(final CommandDto commandDto) {
return null;
}
}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessorForActionAbstract.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessorForActionAbstract.java
similarity index 91%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessorForActionAbstract.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessorForActionAbstract.java
index 97c9780..b887323 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessorForActionAbstract.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessorForActionAbstract.java
@@ -16,8 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.isis.applib.services.command;
+package org.apache.isis.applib.services.commanddto.processor;
+import org.apache.isis.applib.services.command.Command;
import org.apache.isis.schema.cmd.v2.ActionDto;
import org.apache.isis.schema.cmd.v2.CommandDto;
import org.apache.isis.schema.cmd.v2.ParamDto;
@@ -27,9 +28,6 @@ import org.apache.isis.schema.cmd.v2.ParamsDto;
* Convenience adapter for command processors for action invocations.
*/
public abstract class CommandDtoProcessorForActionAbstract implements CommandDtoProcessor {
- protected CommandDto asDto(final Command command) {
- return command.getCommandDto();
- }
protected ActionDto getActionDto(final CommandDto commandDto) {
return (ActionDto) commandDto.getMember();
}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessorForPropertyAbstract.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessorForPropertyAbstract.java
similarity index 88%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessorForPropertyAbstract.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessorForPropertyAbstract.java
index b8e2dd3..e833809 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessorForPropertyAbstract.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessorForPropertyAbstract.java
@@ -16,8 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.isis.applib.services.command;
+package org.apache.isis.applib.services.commanddto.processor;
+import org.apache.isis.applib.services.command.Command;
import org.apache.isis.schema.cmd.v2.CommandDto;
import org.apache.isis.schema.cmd.v2.PropertyDto;
@@ -26,9 +27,6 @@ import org.apache.isis.schema.cmd.v2.PropertyDto;
*/
public abstract class CommandDtoProcessorForPropertyAbstract
implements CommandDtoProcessor {
- protected CommandDto asDto(final Command commandWithDto) {
- return commandWithDto.getCommandDto();
- }
protected PropertyDto getPropertyDto(final CommandDto commandDto) {
return (PropertyDto) commandDto.getMember();
}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/spi/CommandDtoProcessorService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/spi/CommandDtoProcessorService.java
similarity index 61%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/spi/CommandDtoProcessorService.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/spi/CommandDtoProcessorService.java
index 2d0de3d..c645d04 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/spi/CommandDtoProcessorService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/spi/CommandDtoProcessorService.java
@@ -16,12 +16,17 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.isis.applib.services.conmap.command.spi;
+package org.apache.isis.applib.services.commanddto.processor.spi;
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.services.command.Command;
+import javax.annotation.Nullable;
+import javax.inject.Named;
+
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandDto;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto;
import org.apache.isis.schema.cmd.v2.CommandDto;
/**
@@ -32,7 +37,16 @@ import org.apache.isis.schema.cmd.v2.CommandDto;
// tag::refguide[]
public interface CommandDtoProcessorService {
- CommandDto process(final Command command, CommandDto commandDto);
+ /**
+ * @param domainObject - is the target that acts as the source of the
+ * {@link CommandDto}.
+ * @param commandDto - is either <code>null</code>, or is passed from a
+ * previous implementation for further refinement.
+ * @return
+ */
+ CommandDto process(final Object domainObject, @Nullable final CommandDto commandDto);
+
}
// end::refguide[]
+
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/spi/CommandDtoProcessorServiceIdentity.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/spi/CommandDtoProcessorServiceIdentity.java
new file mode 100644
index 0000000..aee41b0
--- /dev/null
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/spi/CommandDtoProcessorServiceIdentity.java
@@ -0,0 +1,23 @@
+package org.apache.isis.applib.services.commanddto.processor.spi;
+
+import javax.inject.Named;
+
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.schema.cmd.v2.CommandDto;
+
+/**
+ * At least one implementation is required.
+ */
+@Service
+@Named("isisApplib.CommandDtoProcessorServiceIdentity")
+@Order(OrderPrecedence.LAST)
+public class CommandDtoProcessorServiceIdentity implements CommandDtoProcessorService {
+
+ @Override
+ public CommandDto process(final Object domainObject, final CommandDto commandDto) {
+ return commandDto;
+ }
+}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/ContentMappingServiceForCommandDto.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/ContentMappingServiceForCommandDto.java
deleted file mode 100644
index 2a6f488..0000000
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/ContentMappingServiceForCommandDto.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.isis.applib.services.conmap.command;
-
-import java.sql.Timestamp;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.ws.rs.core.MediaType;
-
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.context.annotation.Primary;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Service;
-
-import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
-import org.apache.isis.applib.services.conmap.ContentMappingService;
-import org.apache.isis.applib.services.conmap.command.spi.CommandDtoProcessorService;
-import org.apache.isis.applib.services.metamodel.MetaModelService;
-import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.core.commons.internal.exceptions._Exceptions;
-import org.apache.isis.schema.cmd.v2.CommandDto;
-import org.apache.isis.schema.common.v2.PeriodDto;
-
-@Service
-@Named("isisApplib.ContentMappingServiceForCommandDto")
-@Order(OrderPrecedence.EARLY)
-@Primary
-@Qualifier("CommandDto")
-public class ContentMappingServiceForCommandDto implements ContentMappingService {
-
- @Override
- public Object map(Object object, final List<MediaType> acceptableMediaTypes) {
- final boolean supported = Util.isSupported(CommandDto.class, acceptableMediaTypes);
- if(!supported) {
- return null;
- }
-
- return asProcessedDto(object);
- }
-
- /**
- * Not part of the {@link ContentMappingService} API.
- */
- public CommandDto map(final Command command) {
- return asProcessedDto(command);
- }
-
- CommandDto asProcessedDto(final Object object) {
- if (!(object instanceof Command)) {
- return null;
- }
- final Command command = (Command) object;
- return asProcessedDto(command);
- }
-
- private CommandDto asProcessedDto(final Command command) {
- if(command == null) {
- return null;
- }
- CommandDto commandDto = command.getCommandDto();
-
- // global processors
- for (final CommandDtoProcessorService commandDtoProcessorService : commandDtoProcessorServices) {
- commandDto = commandDtoProcessorService.process(command, commandDto);
- if(commandDto == null) {
- // any processor could return null, effectively breaking the chain.
- return null;
- }
- }
-
- // specific processors for this specific member (action or property)
- final CommandDtoProcessor commandDtoProcessor =
- metaModelService.commandDtoProcessorFor(commandDto.getMember().getLogicalMemberIdentifier());
- if (commandDtoProcessor == null) {
- return commandDto;
- }
- return commandDtoProcessor.process(command, commandDto);
- }
-
-
- /**
- * Uses the SPI infrastructure to copy over standard properties from {@link Command} to {@link CommandDto}.
- */
- @Service
- @Named("isisApplib.ContentMappingServiceForCommandDto.CopyOverFromCommand")
- // specify quite a high priority since custom processors will probably want to run after this one
- // (but can choose to run before if they wish)
- @Order(OrderPrecedence.EARLY)
- @Qualifier("Command")
- public static class CopyOverFromCommand implements CommandDtoProcessorService {
-
- @Override
- public CommandDto process(final Command command, CommandDto commandDto) {
-
- // for some reason this isn't being persisted initially, so patch it in. TODO: should fix this
- commandDto.setUser(command.getUsername());
-
- // the timestamp field was only introduced in v1.4 of cmd.xsd, so there's no guarantee
- // it will have been populated. We therefore copy the value in from CommandWithDto entity.
- if(commandDto.getTimestamp() == null) {
- final Timestamp timestamp = command.getTimestamp();
- commandDto.setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(timestamp));
- }
-
- final Bookmark result = command.getResult();
- CommandDtoUtils.setUserData(commandDto,
- UserDataKeys.RESULT, result != null ? result.toString() : null);
- // knowing whether there was an exception is on the master is used to determine whether to
- // continue when replayed on the slave if an exception occurs there also
- Throwable exception = command.getException();
- CommandDtoUtils.setUserData(commandDto,
- UserDataKeys.EXCEPTION,
- _Exceptions.asStacktrace(exception));
-
- PeriodDto timings = CommandDtoUtils.timingsFor(commandDto);
- timings.setStartedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(command.getStartedAt()));
- timings.setCompletedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(command.getCompletedAt()));
-
- return commandDto;
- }
- }
-
-
- @Inject
- MetaModelService metaModelService;
-
- @Inject
- List<CommandDtoProcessorService> commandDtoProcessorServices;
-
-}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/Interaction.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/Interaction.java
index 5cfb218..aba8f10 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/Interaction.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/Interaction.java
@@ -50,7 +50,6 @@ import org.apache.isis.schema.ixn.v2.ObjectCountsDto;
import org.apache.isis.schema.ixn.v2.PropertyEditDto;
import lombok.Getter;
-import lombok.Setter;
import lombok.val;
import lombok.extern.log4j.Log4j2;
@@ -83,8 +82,17 @@ import lombok.extern.log4j.Log4j2;
@Log4j2
public class Interaction implements HasUniqueId {
- @Getter @Setter
- private UUID uniqueId; // <.>
+ public Interaction(final Command command) {
+ this.command = command;
+ }
+
+ @Getter
+ private Command command; // <.>
+
+ @Override
+ public UUID getUniqueId() { // <.>
+ return command.getUniqueId();
+ }
// end::refguide[]
private final List<Execution<?,?>> executionGraphs = _Lists.newArrayList();
@@ -159,16 +167,31 @@ public class Interaction implements HasUniqueId {
* Use the provided {@link MemberExecutor} to invoke an action, with the provided
* {@link ActionInvocation} capturing the details of said action.
* </p>
+ *
+ * <p>
+ * Because this both pushes an {@link Interaction.Execution} to
+ * represent the action invocation and then pops it, that completed
+ * execution is accessible at {@link Interaction#getPriorExecution()}.
+ * </p>
*/
public Object execute(
final MemberExecutor<ActionInvocation> memberExecutor,
final ActionInvocation actionInvocation,
final ClockService clockService,
- final MetricsService metricsService) {
+ final MetricsService metricsService,
+ final Command command) {
- push(actionInvocation);
+ pushAndStart(actionInvocation, clockService, metricsService, command);
+ try {
+ return executeInternal(memberExecutor, actionInvocation);
+ } finally {
+ popAndComplete(clockService, metricsService);
+ }
+ }
- return executeInternal(memberExecutor, actionInvocation, clockService, metricsService);
+ private void pushAndStart(ActionInvocation actionInvocation, ClockService clockService, MetricsService metricsService, Command command) {
+ push(actionInvocation);
+ start(actionInvocation, clockService, metricsService, command);
}
/**
@@ -178,51 +201,50 @@ public class Interaction implements HasUniqueId {
* Use the provided {@link MemberExecutor} to edit a property, with the provided
* {@link PropertyEdit} capturing the details of said property edit.
* </p>
+ *
+ * <p>
+ * Because this both pushes an {@link Interaction.Execution} to
+ * represent the property edit and then pops it, that completed
+ * execution is accessible at {@link Interaction#getPriorExecution()}.
+ * </p>
*/
public Object execute(
final MemberExecutor<PropertyEdit> memberExecutor,
final PropertyEdit propertyEdit,
final ClockService clockService,
- final MetricsService metricsService) {
+ final MetricsService metricsService,
+ final Command command) {
push(propertyEdit);
-
- return executeInternal(memberExecutor, propertyEdit, clockService, metricsService);
+ start(propertyEdit, clockService, metricsService, command);
+ try {
+ return executeInternal(memberExecutor, propertyEdit);
+ } finally {
+ popAndComplete(clockService, metricsService);
+ }
}
- private <T extends Execution<?,?>> Object executeInternal(
- final MemberExecutor<T> memberExecutor,
- final T execution,
- final ClockService clockService,
- final MetricsService metricsService) {
-
- // as a convenience, since in all cases we want the command to start when the first
- // interaction executes, we populate the command here.
+ private <T extends Execution<?,?>> Object executeInternal(MemberExecutor<T> memberExecutor, T execution) {
try {
- try {
- Object result = memberExecutor.execute(execution);
- execution.setReturned(result);
- return result;
- } catch (Exception ex) {
-
- //TODO there is an issue with exceptions getting swallowed, unless this is fixed,
- // we rather print all of them, no matter whether recognized or not later on
- // examples are IllegalArgument- or NullPointer- exceptions being swallowed when using the
- // WrapperFactory utilizing async calls
- log.error("failed to execute an interaction", ex);
-
- // just because an exception has thrown, does not mean it is that significant;
- // it could be that it is recognized by an ExceptionRecognizer and is not severe
- // eg. unique index violation in the DB
- getCurrentExecution().setThrew(ex);
-
- // propagate (as in previous design); caller will need to trap and decide
- throw ex;
- }
- } finally {
- final Timestamp completedAt = clockService.nowAsJavaSqlTimestamp();
- pop(completedAt, metricsService);
+ Object result = memberExecutor.execute(execution);
+ execution.setReturned(result);
+ return result;
+ } catch (Exception ex) {
+
+ //TODO there is an issue with exceptions getting swallowed, unless this is fixed,
+ // we rather print all of them, no matter whether recognized or not later on
+ // examples are IllegalArgument- or NullPointer- exceptions being swallowed when using the
+ // WrapperFactory utilizing async calls
+ log.error("failed to execute an interaction", ex);
+
+ // just because an exception has thrown, does not mean it is that significant;
+ // it could be that it is recognized by an ExceptionRecognizer and is not severe
+ // eg. unique index violation in the DB
+ getCurrentExecution().setThrew(ex);
+
+ // propagate (as in previous design); caller will need to trap and decide
+ throw ex;
}
}
@@ -253,6 +275,20 @@ public class Interaction implements HasUniqueId {
return execution;
}
+ private void start(
+ final Interaction.Execution<?,?> execution,
+ final ClockService clockService,
+ final MetricsService metricsService,
+ final Command command) {
+ // set the startedAt (and update command if this is the top-most member execution)
+ // (this isn't done within Interaction#execute(...) because it requires the DTO
+ // to have been set on the current execution).
+ val startedAt = execution.start(clockService, metricsService);
+ if(command.getStartedAt() == null) {
+ command.updater().setStartedAt(startedAt);
+ }
+ }
+
/**
* <b>NOT API</b>: intended to be called only by the framework.
*
@@ -261,13 +297,16 @@ public class Interaction implements HasUniqueId {
* from the stack of events held by the command.
* </p>
*/
- private Execution<?,?> pop(
- final Timestamp completedAt,
+ private Execution<?,?> popAndComplete(
+ final ClockService clockService,
final MetricsService metricsService) {
+
if(currentExecution == null) {
throw new IllegalStateException("No current execution to pop");
}
final Execution<?,?> popped = currentExecution;
+
+ final Timestamp completedAt = clockService.nowAsJavaSqlTimestamp();
popped.setCompletedAt(completedAt, metricsService);
moveCurrentTo(currentExecution.getParent());
@@ -386,8 +425,18 @@ public class Interaction implements HasUniqueId {
// tag::refguide-2[]
@Getter
private final String targetMember;
-
// end::refguide-2[]
+
+
+ /**
+ * Captures metrics before the Execution Dto is present.
+ */
+ private int numberObjectsLoadedBefore;
+ /**
+ * Captures metrics before the Execution Dto is present.
+ */
+ private int numberObjectsDirtiedBefore;
+
protected Execution(
final Interaction interaction,
final InteractionType interactionType,
@@ -575,15 +624,8 @@ public class Interaction implements HasUniqueId {
final int numberObjectsDirtied) {
execution.startedAt = timestamp;
-
- final MetricsDto metricsDto = metricsFor(execution);
-
- final PeriodDto periodDto = timingsFor(metricsDto);
- periodDto.setStartedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(timestamp));
-
- final ObjectCountsDto objectCountsDto = objectCountsFor(metricsDto);
- numberObjectsLoadedFor(objectCountsDto).setBefore(numberObjectsLoaded);
- numberObjectsDirtiedFor(objectCountsDto).setBefore(numberObjectsDirtied);
+ execution.numberObjectsLoadedBefore = numberObjectsLoaded;
+ execution.numberObjectsDirtiedBefore = numberObjectsLoaded;
}
// tag::refguide-2a[]
@@ -603,9 +645,13 @@ public class Interaction implements HasUniqueId {
final MetricsDto metricsDto = metricsFor(execution);
final PeriodDto periodDto = timingsFor(metricsDto);
- periodDto.setCompletedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(timestamp));
+ periodDto.setStartedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(execution.startedAt));
+ periodDto.setCompletedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(execution.completedAt));
final ObjectCountsDto objectCountsDto = objectCountsFor(metricsDto);
+ numberObjectsLoadedFor(objectCountsDto).setBefore(execution.numberObjectsLoadedBefore);
+ numberObjectsDirtiedFor(objectCountsDto).setBefore(execution.numberObjectsDirtiedBefore);
+
numberObjectsLoadedFor(objectCountsDto).setAfter(numberObjectsLoaded);
numberObjectsDirtiedFor(objectCountsDto).setAfter(numberObjectsDirtied);
}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/InteractionContext.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/InteractionContext.java
index 86cb93c..de41f1e 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/InteractionContext.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/InteractionContext.java
@@ -18,8 +18,10 @@
*/
package org.apache.isis.applib.services.iactn;
+import javax.inject.Inject;
import javax.inject.Named;
+import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
@@ -28,8 +30,15 @@ import org.springframework.stereotype.Service;
import org.apache.isis.applib.annotation.DomainService;
import org.apache.isis.applib.annotation.IsisInteractionScope;
import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.services.TransactionScopeListener;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.services.inject.ServiceInjector;
+import org.apache.isis.applib.services.metrics.MetricsService;
import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
/**
* This service (API and implementation) provides access to context information about any {@link Interaction}.
@@ -41,14 +50,18 @@ import lombok.Getter;
// tag::refguide[]
@Service
@Named("isisApplib.InteractionContext")
-@Order(OrderPrecedence.MIDPOINT)
+@Order(OrderPrecedence.EARLY - 10) // before ChangedObjectService
@Primary
@Qualifier("Default")
@IsisInteractionScope
-//@Log4j2
-public class InteractionContext {
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+@Log4j2
+public class InteractionContext implements TransactionScopeListener, DisposableBean {
// end::refguide[]
+
+ private final MetricsService metricsService;
+
/**
* The currently active {@link Interaction} for this thread.
*/
@@ -65,5 +78,19 @@ public class InteractionContext {
}
// tag::refguide[]
+
+ @Override
+ public void onTransactionEnded() {
+ val command = getInteraction().getCommand();
+ command.updater().setSystemStateChanged(
+ command.isSystemStateChanged() ||
+ metricsService.numberObjectsDirtied() > 0);
+ }
+
+ @Override
+ public void destroy() throws Exception {
+ setInteraction(null);
+ }
+
}
// end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/metamodel/MetaModelService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/metamodel/MetaModelService.java
index feac932..03dd90d 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/metamodel/MetaModelService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/metamodel/MetaModelService.java
@@ -79,7 +79,7 @@ public interface MetaModelService {
BeanSort sortOf(Bookmark bookmark, Mode mode); // <.>
CommandDtoProcessor commandDtoProcessorFor( // <.>
- String memberIdentifier);
+ String logicalMemberIdentifier);
// end::refguide[]
// tag::refguide-1[]
@@ -92,7 +92,7 @@ public interface MetaModelService {
STRICT,
// end::refguide-1[]
/**
- * If the {@link #sortOf(Class, Mode) sort of} object type is unknown, then return {@link Sort#UNKNOWN}.
+ * If the {@link #sortOf(Class, Mode) sort of} object type is unknown, then return {@link BeanSort#UNKNOWN}.
*/
// tag::refguide-1[]
RELAXED
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/tablecol/TableColumnOrderForCollectionTypeAbstract.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/tablecol/TableColumnOrderForCollectionTypeAbstract.java
index 4d604e3..33bc35d 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/tablecol/TableColumnOrderForCollectionTypeAbstract.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/tablecol/TableColumnOrderForCollectionTypeAbstract.java
@@ -34,7 +34,7 @@ public abstract class TableColumnOrderForCollectionTypeAbstract<T>
final String collectionId,
final Class<?> collectionType,
final List<String> propertyIds) {
- if (!this.collectionType.isAssignableFrom(collectionType)) {
+ if (! this.collectionType.isAssignableFrom(collectionType)) {
return propertyIds;
}
return orderParented(parent, collectionId, propertyIds);
@@ -50,7 +50,7 @@ public abstract class TableColumnOrderForCollectionTypeAbstract<T>
public final List<String> orderStandalone(
final Class<?> collectionType,
final List<String> propertyIds) {
- if (this.collectionType.isAssignableFrom(collectionType)) {
+ if (! this.collectionType.isAssignableFrom(collectionType)) {
return propertyIds;
}
return orderStandalone(propertyIds);
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControl.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControl.java
index d5fccec..fa6ab86 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControl.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControl.java
@@ -39,14 +39,18 @@ import lombok.extern.log4j.Log4j2;
@Log4j2
public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
- public static AsyncControl<Void> control() { // <.>
- return new AsyncControl<>();
+ public static AsyncControl<Void> returningVoid() { // <.>
+ return new AsyncControl<>(Void.class);
}
- public static <X> AsyncControl<X> control(final Class<X> clazz) { // <.>
- return new AsyncControl<>();
+ public static <X> AsyncControl<X> returning(final Class<X> cls) { // <.>
+ return new AsyncControl<X>(cls);
}
- private AsyncControl() {
+ @Getter
+ private final Class<R> returnType; // <.>
+
+ private AsyncControl(final Class<R> returnType) {
+ this.returnType = returnType;
with(exception -> { // <.>
log.error(logMessage(), exception);
return null;
@@ -107,9 +111,8 @@ public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
* Contains the result of the invocation. However, if an entity is returned, then the object is automatically
* detached because the persistence session within which it was obtained will have been closed already.
*/
- @Setter(AccessLevel.PACKAGE)
// tag::refguide[]
- @Getter
+ @Getter @Setter
private Future<R> future; // <.>
// end::refguide[]
@@ -127,6 +130,7 @@ public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
}
return buf.toString();
}
+
// tag::refguide[]
// ...
}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControlService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControlService.java
deleted file mode 100644
index c112868..0000000
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControlService.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.isis.applib.services.wrapper.control;
-
-import java.lang.reflect.Method;
-import java.util.concurrent.Future;
-
-import org.springframework.stereotype.Component;
-
-import org.apache.isis.applib.services.bookmark.Bookmark;
-
-import lombok.val;
-
-/**
- * For framework internal use.
- */
-@Component
-public class AsyncControlService {
-
- public <R> AsyncControl<R> init(AsyncControl<R> asyncControl, Method method, Bookmark bookmark) {
- asyncControl.setMethod(method);
- asyncControl.setBookmark(bookmark);
- return asyncControl;
- }
-
- public <R> AsyncControl<R> update(AsyncControl<R> asyncControl, Future<R> future) {
- asyncControl.setFuture(future);
- return asyncControl;
- }
-
- public <R> boolean shouldCheckRules(AsyncControl<R> asyncControl) {
- val executionModes = asyncControl.getExecutionModes();
- val skipRules = executionModes.contains(ExecutionMode.SKIP_RULE_VALIDATION);
- return !skipRules;
- }
-}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/ControlAbstract.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/ControlAbstract.java
index 0716096..2ed93d6 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/ControlAbstract.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/ControlAbstract.java
@@ -38,17 +38,15 @@ public class ControlAbstract<T extends ControlAbstract<T>> {
}
/**
- * Set by framework.
+ * Set by framework; simply used for logging purposes.
*/
- @Setter(AccessLevel.PACKAGE)
- @Getter(AccessLevel.PACKAGE)
+ @Getter(AccessLevel.PACKAGE) @Setter
private Method method;
/**
- * Set by framework.
+ * Set by framework; simply used for logging purposes.
*/
- @Setter(AccessLevel.PACKAGE)
- @Getter(AccessLevel.PACKAGE)
+ @Getter(AccessLevel.PACKAGE) @Setter
private Bookmark bookmark;
// tag::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/IsisModuleApplib.java b/api/applib/src/main/java/org/apache/isis/applib/IsisModuleApplib.java
index 4f3fdca..ada8693 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/IsisModuleApplib.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/IsisModuleApplib.java
@@ -34,6 +34,7 @@ import org.apache.isis.applib.services.bookmark.BookmarkHolder_lookup;
import org.apache.isis.applib.services.bookmark.BookmarkHolder_object;
import org.apache.isis.applib.services.clock.ClockService;
import org.apache.isis.applib.services.command.CommandService;
+import org.apache.isis.applib.services.command.spi.CommandServiceListener;
import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto;
import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandsDto;
import org.apache.isis.applib.services.commanddto.processor.spi.CommandDtoProcessorServiceIdentity;
@@ -73,6 +74,7 @@ import org.apache.isis.schema.IsisModuleSchema;
ClockService.class,
CommandDtoProcessorServiceIdentity.class,
CommandService.class,
+ CommandServiceListener.Null.class,
ContentMappingServiceForCommandDto.class,
ContentMappingServiceForCommandsDto.class,
InteractionContext.class,
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/spi/CommandServiceListener.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/spi/CommandServiceListener.java
index 5d10793..ba6b727 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/spi/CommandServiceListener.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/command/spi/CommandServiceListener.java
@@ -18,8 +18,18 @@
*/
package org.apache.isis.applib.services.command.spi;
+import javax.inject.Named;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
import org.apache.isis.applib.services.command.Command;
+import lombok.extern.log4j.Log4j2;
+
/**
* SPI
*/
@@ -36,5 +46,22 @@ public interface CommandServiceListener {
*/
// tag::refguide[]
void onComplete(final Command command); // <.>
+
+ /**
+ * At least one implementation is required to satisfy injection point into
+ * {@link org.apache.isis.applib.services.command.CommandService}.
+ */
+ @Service
+ @Named("isisApplib.CommandServiceListenerNull")
+ @Order(OrderPrecedence.LATE)
+ @Qualifier("Null")
+ @Log4j2
+ public static class Null implements CommandServiceListener {
+
+ @Override
+ public void onComplete(Command command) {
+
+ }
+ }
}
// end::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControl.java b/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControl.java
index 3019281..fa6ab86 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControl.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControl.java
@@ -39,14 +39,18 @@ import lombok.extern.log4j.Log4j2;
@Log4j2
public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
- public static AsyncControl<Void> control() { // <.>
- return new AsyncControl<>();
+ public static AsyncControl<Void> returningVoid() { // <.>
+ return new AsyncControl<>(Void.class);
}
- public static <X> AsyncControl<X> control(final Class<X> clazz) { // <.>
- return new AsyncControl<>();
+ public static <X> AsyncControl<X> returning(final Class<X> cls) { // <.>
+ return new AsyncControl<X>(cls);
}
- private AsyncControl() {
+ @Getter
+ private final Class<R> returnType; // <.>
+
+ private AsyncControl(final Class<R> returnType) {
+ this.returnType = returnType;
with(exception -> { // <.>
log.error(logMessage(), exception);
return null;
@@ -126,6 +130,7 @@ public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
}
return buf.toString();
}
+
// tag::refguide[]
// ...
}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/ControlAbstract.java b/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/ControlAbstract.java
index 6088154..2ed93d6 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/ControlAbstract.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/ControlAbstract.java
@@ -38,13 +38,13 @@ public class ControlAbstract<T extends ControlAbstract<T>> {
}
/**
- * Set by framework.
+ * Set by framework; simply used for logging purposes.
*/
@Getter(AccessLevel.PACKAGE) @Setter
private Method method;
/**
- * Set by framework.
+ * Set by framework; simply used for logging purposes.
*/
@Getter(AccessLevel.PACKAGE) @Setter
private Bookmark bookmark;
diff --git a/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommandDtoUtils.java b/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommandDtoUtils.java
index 75512e5..8747371 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommandDtoUtils.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommandDtoUtils.java
@@ -152,6 +152,14 @@ public final class CommandDtoUtils {
setUserData(dto, key, bookmark.toString());
}
+ public static void clearUserData(
+ final CommandDto dto, final String key) {
+ if(dto == null || key == null) {
+ return;
+ }
+ userDataFor(dto).getEntry().removeIf(x -> x.getKey().equals(key));
+ }
+
private static MapDto userDataFor(final CommandDto commandDto) {
MapDto userData = commandDto.getUserData();
if(userData == null) {
diff --git a/api/applib/src/test/java/org/apache/isis/applib/services/wrapper/control/AsyncControl_Test.java b/api/applib/src/test/java/org/apache/isis/applib/services/wrapper/control/AsyncControl_Test.java
index 3846167..52e9175 100644
--- a/api/applib/src/test/java/org/apache/isis/applib/services/wrapper/control/AsyncControl_Test.java
+++ b/api/applib/src/test/java/org/apache/isis/applib/services/wrapper/control/AsyncControl_Test.java
@@ -19,7 +19,6 @@
package org.apache.isis.applib.services.wrapper.control;
import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
import org.assertj.core.api.Assertions;
import org.junit.Test;
@@ -34,7 +33,7 @@ public class AsyncControl_Test {
public void defaults() throws Exception {
// given
- val control = AsyncControl.control();
+ val control = AsyncControl.returningVoid();
// then
Assertions.assertThat(control.getExecutionModes()).isEmpty();
@@ -43,7 +42,7 @@ public class AsyncControl_Test {
@Test
public void check_rules() throws Exception {
// given
- val control = AsyncControl.control();
+ val control = AsyncControl.returningVoid();
// when
control.withCheckRules();
@@ -56,7 +55,7 @@ public class AsyncControl_Test {
public void skip_rules() throws Exception {
// given
- val control = AsyncControl.control();
+ val control = AsyncControl.returningVoid();
// when
control.withSkipRules();
@@ -69,7 +68,7 @@ public class AsyncControl_Test {
public void user() throws Exception {
// given
- val control = AsyncControl.control();
+ val control = AsyncControl.returningVoid();
// when
control.withUser("fred");
@@ -82,7 +81,7 @@ public class AsyncControl_Test {
public void roles() throws Exception {
// given
- val control = AsyncControl.control();
+ val control = AsyncControl.returningVoid();
// when
control.withRoles("role-1", "role-2");
@@ -101,7 +100,7 @@ public class AsyncControl_Test {
}));
ExceptionHandler exceptionHandler = ex -> null;
- val control = AsyncControl.control(String.class)
+ val control = AsyncControl.returning(String.class)
.withSkipRules()
.withUser("fred")
.withRoles("role-1", "role-2")
diff --git a/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommandDtoUtils_Test.java b/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommandDtoUtils_Test.java
index eb9dae5..d4e84c3 100644
--- a/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommandDtoUtils_Test.java
+++ b/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommandDtoUtils_Test.java
@@ -62,7 +62,18 @@ public class CommandDtoUtils_Test {
CommandDtoUtils.setUserData(dto, "someKey", "someOtherValue");
assertThat(CommandDtoUtils.getUserData(dto, "someKey"), is("someOtherValue"));
- CommandDtoUtils.setUserData(dto, "someKey", (String)null);
+ }
+
+ @Test
+ public void clearUserData() {
+ // given
+ CommandDtoUtils.setUserData(dto, "someKey", "someOtherValue");
+ assertThat(CommandDtoUtils.getUserData(dto, "someKey"), is("someOtherValue"));
+
+ // when
+ CommandDtoUtils.clearUserData(dto, "someKey");
+
+ // then
assertThat(CommandDtoUtils.getUserData(dto, "someKey"), is(nullValue()));
}
}
\ No newline at end of file
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 950e122..13cc600 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
@@ -22,22 +22,16 @@ import java.lang.reflect.Method;
import org.junit.Test;
-import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.annotation.CommandReification;
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.command.CommandFacetForActionAnnotation;
-import org.apache.isis.core.metamodel.facets.actions.action.command.CommandFacetForActionAnnotationAsConfigured;
-import org.apache.isis.core.metamodel.facets.actions.action.command.CommandFacetFromConfiguration;
import org.apache.isis.core.metamodel.facets.actions.command.CommandFacet;
-import org.apache.isis.core.metamodel.facets.actions.publish.PublishedActionFacet;
-import org.apache.isis.core.metamodel.facets.actions.semantics.ActionSemanticsFacetAbstract;
import lombok.val;
@@ -63,7 +57,7 @@ public class ActionAnnotationFacetFactoryTest_Command extends ActionAnnotationFa
}
@Test
- public void given_annotation_then_facet_added() {
+ public void given_annotation_but_command_not_specified_then_facet_not_added() {
// given
class Customer {
@@ -78,10 +72,49 @@ public class ActionAnnotationFacetFactoryTest_Command extends ActionAnnotationFa
// then
final Facet facet = facetedMethod.getFacet(CommandFacet.class);
+ assertNull(facet);
+ }
+
+ @Test
+ public void given_annotation_with_command_enabled_then_facet_added() {
+
+ // given
+ class Customer {
+ @Action(command = CommandReification.ENABLED)
+ public void someAction() {
+ }
+ }
+ final Method actionMethod = findMethod(Customer.class, "someAction");
+
+ // when
+ processCommand(facetFactory, new ProcessMethodContext(Customer.class, null, actionMethod, mockMethodRemover, facetedMethod));
+
+ // then
+ final Facet facet = facetedMethod.getFacet(CommandFacet.class);
assertNotNull(facet);
assertTrue(facet instanceof CommandFacetForActionAnnotation);
}
+ @Test
+ public void given_annotation_with_command_disabled_then_facet_not_added() {
+
+ // given
+ class Customer {
+ @Action(command = CommandReification.DISABLED)
+ public void someAction() {
+ }
+ }
+ final Method actionMethod = findMethod(Customer.class, "someAction");
+
+ // when
+ processCommand(facetFactory, new ProcessMethodContext(Customer.class, null, actionMethod, mockMethodRemover, facetedMethod));
+
+ // then
+ final Facet facet = facetedMethod.getFacet(CommandFacet.class);
+ assertNull(facet);
+ }
+
+
}
\ No newline at end of file
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFactoryTest.java
index 4ff0732..6c77491 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFactoryTest.java
@@ -19,6 +19,7 @@
package org.apache.isis.core.metamodel.facets.object.domainobjectlayout;
+import org.assertj.core.api.Assertions;
import org.jmock.auto.Mock;
import org.junit.After;
import org.junit.Assert;
@@ -215,7 +216,7 @@ public class DomainObjectLayoutFactoryTest extends AbstractFacetFactoryJUnit4Tes
assertTrue(facet instanceof CssClassFacetForDomainObjectLayoutAnnotation);
final CssClassFacetForDomainObjectLayoutAnnotation facetImpl = (CssClassFacetForDomainObjectLayoutAnnotation) facet;
- Assert.assertThat(facetImpl.cssClass(mockAdapter), is("foobar"));
+ Assertions.assertThat(facetImpl.cssClass(mockAdapter)).isEqualTo("foobar");
expectNoMethodsRemoved();
}
@@ -228,7 +229,7 @@ public class DomainObjectLayoutFactoryTest extends AbstractFacetFactoryJUnit4Tes
facetFactory.process(new FacetFactory.ProcessClassContext(cls, mockMethodRemover, facetHolder));
final Facet facet = facetHolder.getFacet(CssClassFacet.class);
- assertNotNull(facet);
+ assertNull(facet);
expectNoMethodsRemoved();
}
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/value/ImageValueSemanticsProviderAbstractTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/value/ImageValueSemanticsProviderAbstractTest.java
index 386b272..c9ed1a3 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/value/ImageValueSemanticsProviderAbstractTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/value/ImageValueSemanticsProviderAbstractTest.java
@@ -32,7 +32,6 @@ import org.apache.isis.applib.services.inject.ServiceInjector;
import org.apache.isis.core.metamodel.facetapi.FacetHolder;
import org.apache.isis.core.metamodel.facets.value.image.ImageValueSemanticsProviderAbstract;
import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.internaltestsupport.config.IsisConfigurationLegacy;
import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2;
import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2.Mode;
@@ -47,9 +46,6 @@ public class ImageValueSemanticsProviderAbstractTest {
@Mock
private ServiceInjector mockServicesInjector;
- @Mock
- private IsisConfigurationLegacy mockConfiguration;
-
private TestImageSemanticsProvider adapter;
@Before
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
index f3c857a..74d7f0d 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
@@ -62,6 +62,7 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.metamodel.specloader.specimpl.ObjectActionMixedIn;
import org.apache.isis.core.runtime.iactn.IsisInteraction;
import org.apache.isis.core.runtime.iactn.IsisInteractionFactory;
import org.apache.isis.core.runtime.iactn.IsisInteractionTracker;
@@ -204,7 +205,14 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
// it will switch the targetAdapter to be the mixedInAdapter transparently
val argAdapters = argAdaptersFor(actionDto);
- val resultAdapter = objectAction.execute(InteractionHead.simple(targetAdapter)
+ InteractionHead head;
+ if(objectAction instanceof ObjectActionMixedIn) {
+ ObjectActionMixedIn actionMixedIn = (ObjectActionMixedIn) objectAction;
+ head = actionMixedIn.interactionHead(targetAdapter);
+ } else {
+ head = InteractionHead.simple(targetAdapter);
+ }
+ val resultAdapter = objectAction.execute(head
, argAdapters, InteractionInitiatedBy.FRAMEWORK);
// flush any Isis PersistenceCommands pending
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/WrapperFactoryDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/WrapperFactoryDefault.java
index 1676007..de01e1e 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/WrapperFactoryDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/WrapperFactoryDefault.java
@@ -27,6 +27,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.function.BiConsumer;
@@ -43,10 +44,12 @@ import org.springframework.stereotype.Service;
import org.apache.isis.applib.annotation.OrderPrecedence;
import org.apache.isis.applib.services.bookmark.Bookmark;
import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.services.command.Command;
import org.apache.isis.applib.services.command.CommandExecutorService;
import org.apache.isis.applib.services.command.CommandOutcomeHandler;
import org.apache.isis.applib.services.factory.FactoryService;
import org.apache.isis.applib.services.iactn.InteractionContext;
+import org.apache.isis.applib.services.inject.ServiceInjector;
import org.apache.isis.applib.services.metamodel.MetaModelService;
import org.apache.isis.applib.services.repository.RepositoryService;
import org.apache.isis.applib.services.wrapper.WrapperFactory;
@@ -106,6 +109,7 @@ import static org.apache.isis.applib.services.metamodel.MetaModelService.Mode.RE
import static org.apache.isis.applib.services.wrapper.control.SyncControl.control;
import lombok.Data;
+import lombok.RequiredArgsConstructor;
import lombok.val;
/**
@@ -121,17 +125,15 @@ import lombok.val;
//@Log4j2
public class WrapperFactoryDefault implements WrapperFactory {
+ @Inject IsisInteractionTracker isisInteractionTracker;
+
@Inject FactoryService factoryService;
@Inject MetaModelContext metaModelContext;
@Inject SpecificationLoader specificationLoader;
- @Inject IsisInteractionTracker isisInteractionTracker;
- @Inject IsisInteractionFactory isisInteractionFactory;
@Inject Provider<InteractionContext> interactionContextProvider;
- @Inject TransactionService transactionService;
- @Inject CommandExecutorService commandExecutorService;
+ @Inject ServiceInjector serviceInjector;
@Inject ProxyFactoryService proxyFactoryService; // protected to allow JUnit test
@Inject CommandDtoServiceInternal commandDtoServiceInternal;
- @Inject BookmarkService bookmarkService;
private final List<InteractionListener> listeners = new ArrayList<>();
private final Map<Class<? extends InteractionEvent>, InteractionEventDispatcher>
@@ -306,6 +308,7 @@ public class WrapperFactoryDefault implements WrapperFactory {
val isisInteraction = currentIsisInteraction();
val asyncAuthSession = authSessionFrom(asyncControl, isisInteraction.getAuthenticationSession());
+ val command = interactionContextProvider.get().getInteraction().getCommand();
val targetAdapter = memberAndTarget.getTarget();
val method = memberAndTarget.getMethod();
@@ -333,35 +336,15 @@ public class WrapperFactoryDefault implements WrapperFactory {
asyncControl.setBookmark(Bookmark.from(oidDto));
val executorService = asyncControl.getExecutorService();
- val future = executorService.submit(() ->
- isisInteractionFactory.callAuthenticated(asyncAuthSession, () ->
- transactionService.executeWithinTransaction(() -> {
- val bookmark = commandExecutorService.executeCommand(commandDto, CommandOutcomeHandler.NULL);
- if (bookmark != null) {
- Object entity = bookmarkService.lookup(bookmark);
- val metaModelService = WrapperFactoryDefault.this.getMetaModelService();
- if (metaModelService.sortOf(bookmark, RELAXED).isEntity()) {
- entity = WrapperFactoryDefault.this.getRepositoryService().detach(entity);
- }
- return entity;
- }
- return null;
- })
- ));
+ val future = executorService.submit(
+ new ExecCommand(asyncAuthSession, commandDto, asyncControl.getReturnType(), command, serviceInjector)
+ );
- asyncControl.setFuture((Future<R>) future);
+ asyncControl.setFuture(future);
return null;
}
- private RepositoryService getRepositoryService() {
- return metaModelContext.getRepositoryService();
- }
-
- private MetaModelService getMetaModelService() {
- return metaModelContext.getServiceRegistry().lookupServiceElseFail(MetaModelService.class);
- }
-
private <T> MemberAndTarget forRegular(Method method, T domainObject) {
val targetObjSpec = (ObjectSpecificationDefault) specificationLoader.loadSpecification(method.getDeclaringClass());
@@ -513,4 +496,42 @@ public class WrapperFactoryDefault implements WrapperFactory {
return currentIsisInteraction().getObjectManager();
}
+ @RequiredArgsConstructor
+ private static class ExecCommand<R> implements Callable<R> {
+
+ private final AuthenticationSession authenticationSession;
+ private final CommandDto commandDto;
+ private final Class<R> returnType;
+ private final Command parentCommand;
+ private final ServiceInjector serviceInjector;
+
+ @Inject IsisInteractionFactory isisInteractionFactory;
+ @Inject TransactionService transactionService;
+ @Inject CommandExecutorService commandExecutorService;
+ @Inject Provider<InteractionContext> interactionContextProvider;
+ @Inject BookmarkService bookmarkService;
+ @Inject RepositoryService repositoryService;
+ @Inject MetaModelService metaModelService;
+
+ @Override
+ public R call() {
+ serviceInjector.injectServicesInto(this);
+
+ return isisInteractionFactory.callAuthenticated(authenticationSession, () -> {
+ val childCommand = interactionContextProvider.get().getInteraction().getCommand();
+ childCommand.updater().setParent(parentCommand);
+ return transactionService.executeWithinTransaction(() -> {
+ val bookmark = commandExecutorService.executeCommand(commandDto, CommandOutcomeHandler.NULL);
+ if (bookmark == null) {
+ return null;
+ }
+ R entity = bookmarkService.lookup(bookmark, returnType);
+ if (metaModelService.sortOf(bookmark, RELAXED).isEntity()) {
+ entity = repositoryService.detach(entity);
+ }
+ return entity;
+ });
+ });
+ }
+ }
}
diff --git a/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/wrapper/WrapperFactoryDefault_wrappedObject_Test.java b/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/wrapper/WrapperFactoryDefault_wrappedObject_Test.java
deleted file mode 100644
index 2df1b2d..0000000
--- a/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/wrapper/WrapperFactoryDefault_wrappedObject_Test.java
+++ /dev/null
@@ -1,503 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.isis.core.runtimeservices.wrapper;
-
-import java.lang.reflect.Method;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Stream;
-
-import org.jmock.Expectations;
-import org.jmock.auto.Mock;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import org.apache.isis.applib.Identifier;
-import org.apache.isis.applib.services.bookmark.BookmarkService;
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandExecutorService;
-import org.apache.isis.applib.services.factory.FactoryService;
-import org.apache.isis.applib.services.iactn.Interaction;
-import org.apache.isis.applib.services.iactn.InteractionContext;
-import org.apache.isis.applib.services.message.MessageService;
-import org.apache.isis.applib.services.metamodel.BeanSort;
-import org.apache.isis.applib.services.wrapper.DisabledException;
-import org.apache.isis.applib.services.wrapper.HiddenException;
-import org.apache.isis.applib.services.wrapper.InvalidException;
-import org.apache.isis.applib.services.xactn.TransactionService;
-import org.apache.isis.core.codegen.bytebuddy.services.ProxyFactoryServiceByteBuddy;
-import org.apache.isis.core.commons.internal.plugins.codegen.ProxyFactoryService;
-import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2;
-import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2.Mode;
-import org.apache.isis.core.metamodel._testing.MetaModelContext_forTesting;
-import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.facetapi.FacetUtil;
-import org.apache.isis.core.metamodel.facets.FacetedMethod;
-import org.apache.isis.core.metamodel.facets.all.named.NamedFacetInferred;
-import org.apache.isis.core.metamodel.facets.members.disabled.method.DisableForContextFacetViaMethod;
-import org.apache.isis.core.metamodel.facets.members.hidden.method.HideForContextFacetViaMethod;
-import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
-import org.apache.isis.core.metamodel.facets.properties.accessor.PropertyAccessorFacetViaAccessor;
-import org.apache.isis.core.metamodel.facets.properties.update.clear.PropertyClearFacetViaClearMethod;
-import org.apache.isis.core.metamodel.facets.properties.update.init.PropertyInitializationFacetViaSetterMethod;
-import org.apache.isis.core.metamodel.facets.properties.update.modify.PropertySetterFacetViaModifyMethod;
-import org.apache.isis.core.metamodel.facets.properties.validating.method.PropertyValidateFacetViaMethod;
-import org.apache.isis.core.metamodel.interactions.HidingInteractionAdvisor;
-import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
-import org.apache.isis.core.metamodel.services.command.CommandDtoServiceInternal;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
-import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-import org.apache.isis.core.metamodel.specloader.specimpl.OneToOneAssociationDefault;
-import org.apache.isis.core.metamodel.specloader.specimpl.dflt.ObjectSpecificationDefault;
-import org.apache.isis.core.runtime.iactn.IsisInteractionFactory;
-import org.apache.isis.core.runtime.iactn.IsisInteractionTracker;
-import org.apache.isis.core.runtimeservices.wrapper.dom.employees.Employee;
-import org.apache.isis.core.runtimeservices.wrapper.dom.employees.EmployeeRepository;
-import org.apache.isis.core.runtimeservices.wrapper.dom.employees.EmployeeRepositoryImpl;
-import org.apache.isis.core.security.authentication.AuthenticationSessionTracker;
-import org.apache.isis.core.security.authentication.standard.SimpleSession;
-import org.apache.isis.schema.cmd.v2.CommandDto;
-
-import lombok.val;
-
-public class WrapperFactoryDefault_wrappedObject_Test {
-
- @Rule
- public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Mock private AuthenticationSessionTracker mockAuthenticationSessionTracker;
- @Mock private MessageService mockMessageService;
- @Mock private InteractionContext mockInteractionContext;
- @Mock private Interaction mockInteraction;
- @Mock private Command mockCommand;
- @Mock private CommandDtoServiceInternal mockCommandDtoServiceInternal;
- @Mock private ObjectSpecification mockOnType;
- @Mock private SpecificationLoader mockSpecificationLoader;
- @Mock private IsisInteractionFactory mockIsisInteractionFactory;
- @Mock private IsisInteractionTracker mockIsisInteractionTracker;
- @Mock private CommandExecutorService mockCommandExecutorService;
- @Mock private ObjectSpecificationDefault mockEmployeeSpec;
- @Mock private FactoryService mockFactoryService;
- @Mock private TransactionService mockTransactionService;
- @Mock private BookmarkService mockBookmarkService;
- @Mock protected ObjectManager mockObjectManager;
-
- private ObjectMember employeeNameMember;
-
- @Mock private ObjectSpecificationDefault mockStringSpec;
- @Mock private ManagedObject mockEmployeeAdapter;
- @Mock private ManagedObject mockAdapterForStringSmith;
- @Mock private ManagedObject mockAdapterForStringJones;
- @Mock private Identifier mockId;
-
- private final SimpleSession session = new SimpleSession("tester", Collections.<String>emptyList());
-
- private EmployeeRepository employeeRepository;
-
- private Employee employeeDO;
- private Employee employeeWO;
-
- private WrapperFactoryDefault wrapperFactory;
-
- protected MetaModelContext metaModelContext;
-
- @SuppressWarnings("unchecked")
- @Before
- public void setUp() {
-
- // PRODUCTION
-
- val proxyFactoryService = (ProxyFactoryService) new ProxyFactoryServiceByteBuddy();
-
- metaModelContext = MetaModelContext_forTesting.builder()
- .specificationLoader(mockSpecificationLoader)
- .objectManager(mockObjectManager)
- .authenticationSessionTracker(mockAuthenticationSessionTracker)
- .singleton(proxyFactoryService)
- .singleton(wrapperFactory = createWrapperFactory(proxyFactoryService))
- .singleton(mockCommandDtoServiceInternal)
- .singleton(mockFactoryService)
- .singleton(mockIsisInteractionFactory)
- .singleton(mockIsisInteractionTracker)
- .singleton(mockTransactionService)
- .singleton(mockCommandExecutorService)
- .singleton(mockCommandDtoServiceInternal)
- .singleton(mockBookmarkService)
- .build();
-
- metaModelContext.getServiceInjector().injectServicesInto(wrapperFactory);
-
- employeeRepository = new EmployeeRepositoryImpl();
-
- employeeDO = new Employee();
- employeeDO.setName("Smith");
- employeeDO.setEmployeeRepository(employeeRepository);
-
- context.checking(new Expectations() {
- {
-
- allowing(mockObjectManager).adapt(employeeDO);
- will(returnValue(mockEmployeeAdapter));
-
- allowing(mockEmployeeSpec).isManagedBean();
- will(returnValue(true));
-
- allowing(mockEmployeeSpec).getBeanSort();
- will(returnValue(BeanSort.ENTITY));
-
- allowing(mockEmployeeSpec).isIdentifiable();
- will(returnValue(true));
-
- allowing(mockEmployeeSpec).getCorrespondingClass();
- will(returnValue(Employee.class));
-
- allowing(mockStringSpec).getCorrespondingClass();
- will(returnValue(String.class));
-
- allowing(mockCommandDtoServiceInternal).asCommandDto(with(any(List.class)), with(any(OneToOneAssociation.class)), with(any(ManagedObject.class)));
- will(returnValue(new CommandDto()));
-
- allowing(mockInteractionContext).getInteraction();
- will(returnValue(mockInteraction));
-
- allowing(mockInteraction).getCommand();
- will(returnValue(mockCommand));
-
- allowing(mockSpecificationLoader).loadSpecification(String.class);
- will(returnValue(mockStringSpec));
-
- allowing(mockStringSpec).getShortIdentifier();
- will(returnValue(String.class.getName()));
-
- allowing(mockAuthenticationSessionTracker).currentAuthenticationSession();
- will(returnValue(Optional.of(session)));
-
- allowing(mockEmployeeAdapter).titleString(null);
- will(returnValue("titleOf[mockEmployeeAdapter]"));
-
- allowing(mockEmployeeAdapter).getSpecification();
- will(returnValue(mockEmployeeSpec));
-
- allowing(mockSpecificationLoader).loadSpecification(Employee.class);
- will(returnValue(mockEmployeeSpec));
-
- allowing(mockEmployeeSpec).getMember(methodOf(Employee.class, "getEmployeeRepository"));
- will(returnValue(null));
-
- allowing(mockEmployeeSpec).getFacet(EntityFacet.class);
- will(returnValue(null));
-
-
-// allowing(mockAdapterForStringJones).isDestroyed();
-// will(returnValue(false));
-
- allowing(mockAdapterForStringJones).getSpecification();
- will(returnValue(mockStringSpec));
-
- allowing(mockObjectManager).adapt("Jones");
- will(returnValue(mockAdapterForStringJones));
-
- }
- });
-
-
- final Method employeeGetNameMethod = methodOf(Employee.class, "getName");
- final Method employeeSetNameMethod = methodOf(Employee.class, "setName", String.class);
- final Method employeeModifyNameMethod = methodOf(Employee.class, "modifyName", String.class);
- final Method employeeHideNameMethod = methodOf(Employee.class, "hideName");
- final Method employeeDisableNameMethod = methodOf(Employee.class, "disableName");
- final Method employeeValidateNameMethod = methodOf(Employee.class, "validateName", String.class);
- final Method employeeClearNameMethod = methodOf(Employee.class, "clearName");
- employeeNameMember = new OneToOneAssociationDefault(
- facetedMethodForProperty(
- metaModelContext,
- employeeSetNameMethod, employeeGetNameMethod, employeeModifyNameMethod, employeeClearNameMethod, employeeHideNameMethod, employeeDisableNameMethod, employeeValidateNameMethod));
-
- context.checking(new Expectations() {
- {
- // allowing(mockServicesInjector).lookupServiceElseFail(WrapperFactory.class);
- // will(returnValue(wrapperFactory));
-
- allowing(mockEmployeeSpec).getMember(employeeGetNameMethod);
- will(returnValue(employeeNameMember));
-
- allowing(mockEmployeeSpec).getMember(employeeSetNameMethod);
- will(returnValue(employeeNameMember));
-
- allowing(mockEmployeeSpec).getMember(employeeModifyNameMethod);
- will(returnValue(employeeNameMember));
-
- allowing(mockEmployeeSpec).getMember(employeeClearNameMethod);
- will(returnValue(employeeNameMember));
-
- allowing(mockEmployeeAdapter).getPojo();
- will(returnValue(employeeDO));
-
-// allowing(mockEmployeeAdapter).isRepresentingPersistent();
-// will(returnValue(true));
- }
- });
-
-
- employeeWO = wrapperFactory.wrap(employeeDO);
- }
-
- protected WrapperFactoryDefault createWrapperFactory(ProxyFactoryService proxyFactoryService) {
- val wrapperFactory = new WrapperFactoryDefault();
- wrapperFactory.proxyFactoryService = proxyFactoryService;
- wrapperFactory.init();
- return wrapperFactory;
- }
-
- @Test
- public void shouldWrapDomainObject() {
- // then
- assertThat(employeeWO, is(notNullValue()));
- }
-
- @Test
- public void shouldBeAbleToInjectIntoDomainObjects() {
- assertThat(employeeDO.getEmployeeRepository(), is(notNullValue()));
- }
-
- @Test
- public void cannotAccessMethodNotCorrespondingToMember() {
-
- expectedException.expectMessage(
- "Method 'getEmployeeRepository' being invoked does not correspond to any of the object's fields or actions.");
-
- // then
- assertThat(employeeWO.getEmployeeRepository(), is(notNullValue()));
- }
-
- @Test
- public void shouldBeAbleToReadVisibleProperty() {
-
- allowingEmployeeHasSmithAdapter();
-
- assertTrue(
- metaModelContext.getConfiguration().getCore().getMetaModel().isFilterVisibility());
-
- context.checking(new Expectations() {{
-
- allowing(mockAdapterForStringSmith).getSpecification();
- will(returnValue(mockStringSpec));
-
- allowing(mockStringSpec).isEntity();
- will(returnValue(false));
-
- allowing(mockStringSpec).getIdentifier();
- will(returnValue(mockId));
-
- allowing(mockStringSpec).getBeanSort();
- will(returnValue(BeanSort.VIEW_MODEL));
-
- allowing(mockStringSpec).streamFacets(HidingInteractionAdvisor.class);
- will(returnValue(Stream.empty()));
-
- allowing(mockObjectManager).adapt("Smith");
- will(returnValue(mockAdapterForStringSmith));
- }});
-
- // then
- assertThat(employeeWO.getName(), is(employeeDO.getName()));
- }
-
- @Test
- public void shouldNotBeAbleToViewHiddenProperty() {
-
- expectedException.expect(HiddenException.class);
-
- allowingEmployeeHasSmithAdapter();
-
- // given
- employeeDO.whetherHideName = true;
- // when
- employeeWO.getName();
- // then should throw exception
- }
-
- @Test
- public void shouldBeAbleToModifyEnabledPropertyUsingSetter() {
-
- allowingJonesStringValueAdapter();
-
- assertTrue(
- metaModelContext.getConfiguration().getCore().getMetaModel().isFilterVisibility());
-
- context.checking(new Expectations() {
- {
- allowing(mockAdapterForStringJones).titleString(null);
-
- ignoring(mockCommand);
-
- allowing(mockStringSpec).isParented();
- will(returnValue(false));
-
- allowing(mockStringSpec).isEntity();
- will(returnValue(false));
-
- allowing(mockStringSpec).getIdentifier();
- will(returnValue(mockId));
-
- allowing(mockStringSpec).getBeanSort();
- will(returnValue(BeanSort.VIEW_MODEL));
-
- allowing(mockEmployeeSpec).isViewModelCloneable(mockEmployeeAdapter);
- will(returnValue(false));
-
- allowing(mockStringSpec).streamFacets(HidingInteractionAdvisor.class);
- will(returnValue(Stream.empty()));
-
- }
- });
-
- // when
- employeeWO.setName("Jones");
- // then
- assertThat(employeeDO.getName(), is("Jones"));
- assertThat(employeeWO.getName(), is(employeeDO.getName()));
- }
-
- @Test
- public void shouldNotBeAbleToModifyDisabledProperty() {
-
- expectedException.expect(DisabledException.class);
-
- // given
- employeeDO.reasonDisableName = "sorry, no change allowed";
- // when
- employeeWO.setName("Jones");
- // then should throw exception
- }
-
- @Test
- public void shouldNotBeAbleToModifyPropertyUsingModify() {
-
- allowingJonesStringValueAdapter();
-
- expectedException.expect(UnsupportedOperationException.class);
-
- // when
- employeeWO.modifyName("Jones");
- // then should throw exception
- }
-
- @Test
- public void shouldNotBeAbleToModifyPropertyUsingClear() {
-
- expectedException.expect(UnsupportedOperationException.class);
-
- // when
- employeeWO.clearName();
- // then should throw exception
- }
-
- @Test
- public void shouldNotBeAbleToModifyPropertyIfInvalid() {
-
- allowingJonesStringValueAdapter();
-
- expectedException.expect(InvalidException.class);
-
- // given
- employeeDO.reasonValidateName = "sorry, invalid data";
- // when
- employeeWO.setName("Jones");
- // then should throw exception
- }
-
-
- // //////////////////////////////////////
-
- private FacetedMethod facetedMethodForProperty(
- MetaModelContext mmc,
- Method init, Method accessor, Method modify, Method clear, Method hide, Method disable, Method validate) {
- FacetedMethod facetedMethod = FacetedMethod.createForProperty(accessor.getDeclaringClass(), accessor);
- facetedMethod.setMetaModelContext(mmc);
- FacetUtil.addFacet(new PropertyAccessorFacetViaAccessor(mockOnType, accessor, facetedMethod));
- FacetUtil.addFacet(new PropertyInitializationFacetViaSetterMethod(init, facetedMethod));
- FacetUtil.addFacet(new PropertySetterFacetViaModifyMethod(modify, facetedMethod));
- FacetUtil.addFacet(new PropertyClearFacetViaClearMethod(clear, facetedMethod));
- FacetUtil.addFacet(new HideForContextFacetViaMethod(hide, facetedMethod));
- FacetUtil.addFacet(new DisableForContextFacetViaMethod(disable, null, null, facetedMethod));
- FacetUtil.addFacet(new PropertyValidateFacetViaMethod(validate, null, null, facetedMethod));
- FacetUtil.addFacet(new NamedFacetInferred(accessor.getName(), facetedMethod));
- return facetedMethod;
- }
-
- private static Method methodOf(Class<?> cls, String methodName) {
- return methodOf(cls, methodName, new Class<?>[]{});
- }
-
- private static Method methodOf(Class<?> cls, String methodName, Class<?>... args) {
- try {
- return cls.getMethod(methodName, args);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- // //////////////////////////////////////
-
- private void allowingEmployeeHasSmithAdapter() {
- context.checking(new Expectations() {
- {
-// allowing(mockAdapterManager).adapterFor("Smith");
-// will(returnValue(mockAdapterForStringSmith));
-
- allowing(mockAdapterForStringSmith).getPojo();
- will(returnValue("Smith"));
- }
- });
- }
-
- private void allowingJonesStringValueAdapter() {
- context.checking(new Expectations() {
- {
-// allowing(mockAdapterManager).adapterFor("Jones");
-// will(returnValue(mockAdapterForStringJones));
-
- allowing(mockAdapterForStringJones).getPojo();
- will(returnValue("Jones"));
-
-// allowing(mockAdapterForStringJones).isTransient();
-// will(returnValue(false));
- }
- });
- }
-
-
-
-}
diff --git a/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/wrapper/WrapperFactoryDefault_wrappedObject_transient_Test.java b/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/wrapper/WrapperFactoryDefault_wrappedObject_transient_Test.java
deleted file mode 100644
index e1a94c2..0000000
--- a/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/wrapper/WrapperFactoryDefault_wrappedObject_transient_Test.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.isis.core.runtimeservices.wrapper;
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-
-import org.jmock.Expectations;
-import org.jmock.auto.Mock;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import org.apache.isis.applib.Identifier;
-import org.apache.isis.applib.annotation.Where;
-import org.apache.isis.applib.services.bookmark.BookmarkService;
-import org.apache.isis.applib.services.command.CommandExecutorService;
-import org.apache.isis.applib.services.factory.FactoryService;
-import org.apache.isis.applib.services.inject.ServiceInjector;
-import org.apache.isis.applib.services.message.MessageService;
-import org.apache.isis.applib.services.registry.ServiceRegistry;
-import org.apache.isis.applib.services.wrapper.DisabledException;
-import org.apache.isis.applib.services.wrapper.events.PropertyModifyEvent;
-import org.apache.isis.applib.services.wrapper.events.PropertyUsabilityEvent;
-import org.apache.isis.applib.services.wrapper.events.PropertyVisibilityEvent;
-import org.apache.isis.applib.services.xactn.TransactionService;
-import org.apache.isis.core.codegen.bytebuddy.services.ProxyFactoryServiceByteBuddy;
-import org.apache.isis.core.commons.internal.plugins.codegen.ProxyFactoryService;
-import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2;
-import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2.Mode;
-import org.apache.isis.core.metamodel._testing.MetaModelContext_forTesting;
-import org.apache.isis.core.metamodel.consent.Allow;
-import org.apache.isis.core.metamodel.consent.Consent;
-import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
-import org.apache.isis.core.metamodel.consent.InteractionResult;
-import org.apache.isis.core.metamodel.consent.Veto;
-import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.facetapi.Facet;
-import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet;
-import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacetAbstractAlwaysEverywhere;
-import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
-import org.apache.isis.core.metamodel.facets.properties.accessor.PropertyAccessorFacetViaAccessor;
-import org.apache.isis.core.metamodel.facets.properties.update.modify.PropertySetterFacetViaSetterMethod;
-import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
-import org.apache.isis.core.metamodel.services.command.CommandDtoServiceInternal;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-import org.apache.isis.core.metamodel.specloader.specimpl.dflt.ObjectSpecificationDefault;
-import org.apache.isis.core.runtime.iactn.IsisInteractionFactory;
-import org.apache.isis.core.runtime.iactn.IsisInteractionTracker;
-import org.apache.isis.core.runtimeservices.wrapper.dom.employees.Employee;
-import org.apache.isis.core.security.authentication.AuthenticationSessionTracker;
-import org.apache.isis.core.security.authentication.standard.SimpleSession;
-
-import static org.apache.isis.core.internaltestsupport.jmocking.PostponedAction.returnValuePostponed;
-
-import lombok.val;
-
-/**
- * Contract test.
- */
-public class WrapperFactoryDefault_wrappedObject_transient_Test {
-
- @Rule
- public final JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
-
- @Mock private AuthenticationSessionTracker mockAuthenticationSessionTracker;
- @Mock private MessageService mockMessageService;
- @Mock private CommandDtoServiceInternal mockCommandDtoServiceInternal;
- @Mock private ObjectSpecification mockOnType;
- @Mock private SpecificationLoader mockSpecificationLoader;
- @Mock private IsisInteractionFactory mockIsisInteractionFactory;
- @Mock private IsisInteractionTracker mockIsisInteractionTracker;
- @Mock private CommandExecutorService mockCommandExecutorService;
- @Mock private FactoryService mockFactoryService;
- @Mock private TransactionService mockTransactionService;
-
- @Mock private ManagedObject mockEmployeeAdapter;
- @Mock private ObjectSpecificationDefault mockEmployeeSpec;
- @Mock private OneToOneAssociation mockPasswordMember;
- @Mock private Identifier mockPasswordIdentifier;
- @Mock private ServiceInjector mockServiceInjector;
- @Mock private ServiceRegistry mockServiceRegistry;
- @Mock private BookmarkService mockBookmarkService;
- @Mock protected ManagedObject mockPasswordAdapter;
- @Mock protected ObjectManager mockObjectManager;
-
- private Employee employeeDO;
-
- private final String passwordValue = "12345678";
-
- private final SimpleSession session = new SimpleSession("tester", Collections.<String>emptyList());
-
- private Method getPasswordMethod;
- private Method setPasswordMethod;
-
- private WrapperFactoryDefault wrapperFactory;
- private Employee employeeWO;
- private List<Facet> facets;
-
- protected MetaModelContext metaModelContext;
-
- @Before
- public void setUp() throws Exception {
-
- // PRODUCTION
-
- val proxyFactoryService = (ProxyFactoryService) new ProxyFactoryServiceByteBuddy();
-
- metaModelContext = MetaModelContext_forTesting.builder()
- .specificationLoader(mockSpecificationLoader)
- .objectManager(mockObjectManager)
- .authenticationSessionTracker(mockAuthenticationSessionTracker)
- .singleton(proxyFactoryService)
- .singleton(wrapperFactory = createWrapperFactory(proxyFactoryService))
- .singleton(mockFactoryService)
- .singleton(mockIsisInteractionFactory)
- .singleton(mockIsisInteractionTracker)
- .singleton(mockTransactionService)
- .singleton(mockCommandExecutorService)
- .singleton(mockCommandDtoServiceInternal)
- .singleton(mockBookmarkService)
- .build();
-
- metaModelContext.getServiceInjector().injectServicesInto(wrapperFactory);
-
- employeeDO = new Employee();
- employeeDO.setName("Smith");
-
- getPasswordMethod = Employee.class.getMethod("getPassword");
- setPasswordMethod = Employee.class.getMethod("setPassword", String.class);
-
- context.checking(new Expectations() {
- {
-
- allowing(mockObjectManager).adapt(employeeDO);
- will(returnValue(mockEmployeeAdapter));
-
- allowing(mockObjectManager).adapt(passwordValue);
- will(returnValue(mockPasswordAdapter));
-
- allowing(mockEmployeeAdapter).getSpecification();
- will(returnValue(mockEmployeeSpec));
-
- allowing(mockEmployeeAdapter).getPojo();
- will(returnValue(employeeDO));
-
- allowing(mockPasswordAdapter).getPojo();
- will(returnValue(passwordValue));
-
- allowing(mockPasswordMember).getIdentifier();
- will(returnValue(mockPasswordIdentifier));
-
- allowing(mockPasswordIdentifier).toClassAndNameIdentityString();
- will(returnValue("mocked-class#member"));
-
- allowing(mockSpecificationLoader).loadSpecification(Employee.class);
- will(returnValue(mockEmployeeSpec));
-
- allowing(mockEmployeeSpec).getMember(with(setPasswordMethod));
- will(returnValue(mockPasswordMember));
-
- allowing(mockEmployeeSpec).getMember(with(getPasswordMethod));
- will(returnValue(mockPasswordMember));
-
- allowing(mockEmployeeSpec).getFacet(EntityFacet.class);
- will(returnValue(null));
-
- allowing(mockPasswordMember).getName();
- will(returnValue("password"));
-
- allowing(mockAuthenticationSessionTracker).currentAuthenticationSession();
- will(returnValue(Optional.of(session)));
-
- allowing(mockPasswordMember).isOneToOneAssociation();
- will(returnValue(true));
-
- allowing(mockPasswordMember).isOneToManyAssociation();
- will(returnValue(false));
-
- }
- });
-
- employeeWO = wrapperFactory.wrap(employeeDO);
- }
-
- protected WrapperFactoryDefault createWrapperFactory(ProxyFactoryService proxyFactoryService) {
- val wrapperFactory = new WrapperFactoryDefault();
- wrapperFactory.proxyFactoryService = proxyFactoryService;
- wrapperFactory.init();
- return wrapperFactory;
- }
-
- @Test(expected = DisabledException.class)
- public void shouldNotBeAbleToModifyProperty() {
-
- // given
- final DisabledFacet disabledFacet = new DisabledFacetAbstractAlwaysEverywhere(mockPasswordMember){};
- facets = Arrays.asList(disabledFacet, new PropertySetterFacetViaSetterMethod(setPasswordMethod, mockPasswordMember));
-
- final Consent visibilityConsent = new Allow(new InteractionResult(new PropertyVisibilityEvent(employeeDO, null)));
-
- final InteractionResult usabilityInteractionResult = new InteractionResult(new PropertyUsabilityEvent(employeeDO, null));
- usabilityInteractionResult.advise("disabled", disabledFacet);
- final Consent usabilityConsent = new Veto(usabilityInteractionResult);
-
- context.checking(new Expectations() {
- {
- allowing(mockPasswordMember).streamFacets();
- will(returnValuePostponed(facets::stream));
-
- allowing(mockPasswordMember).isVisible(mockEmployeeAdapter, InteractionInitiatedBy.USER, Where.ANYWHERE);
- will(returnValue(visibilityConsent));
-
- allowing(mockPasswordMember).isUsable(mockEmployeeAdapter, InteractionInitiatedBy.USER, Where.ANYWHERE);
- will(returnValue(usabilityConsent));
- }
- });
-
- // when
- employeeWO.setPassword(passwordValue);
-
- // then should throw exception
- }
-
- @Test
- public void canModifyProperty() {
- // given
-
- final Consent visibilityConsent = new Allow(new InteractionResult(new PropertyVisibilityEvent(employeeDO, mockPasswordIdentifier)));
- final Consent usabilityConsent = new Allow(new InteractionResult(new PropertyUsabilityEvent(employeeDO, mockPasswordIdentifier)));
- final Consent validityConsent = new Allow(new InteractionResult(new PropertyModifyEvent(employeeDO, mockPasswordIdentifier, passwordValue)));
-
- context.checking(new Expectations() {
- {
- allowing(mockPasswordMember).isVisible(mockEmployeeAdapter, InteractionInitiatedBy.USER, Where.ANYWHERE);
- will(returnValue(visibilityConsent));
-
- allowing(mockPasswordMember).isUsable(mockEmployeeAdapter, InteractionInitiatedBy.USER, Where.ANYWHERE);
- will(returnValue(usabilityConsent));
-
- allowing(mockPasswordMember).isAssociationValid(mockEmployeeAdapter, mockPasswordAdapter,
- InteractionInitiatedBy.USER);
- will(returnValue(validityConsent));
- }
- });
-
- facets = Arrays.asList((Facet)new PropertySetterFacetViaSetterMethod(
- setPasswordMethod, mockPasswordMember));
-
- context.checking(new Expectations() {
- {
- allowing(mockPasswordMember).streamFacets();
- will(returnValuePostponed(()->facets.stream()));
-
- oneOf(mockPasswordMember)
- .set(mockEmployeeAdapter, mockPasswordAdapter, InteractionInitiatedBy.USER);
-
- oneOf(mockPasswordMember).get(mockEmployeeAdapter, InteractionInitiatedBy.USER);
- will(returnValue(mockPasswordAdapter));
- }
- });
-
- // when
- employeeWO.setPassword(passwordValue);
-
- // and given
- facets = Arrays.asList((Facet)new PropertyAccessorFacetViaAccessor(
- mockOnType, getPasswordMethod, mockPasswordMember));
-
- // then be allowed
- assertThat(employeeWO.getPassword(), is(passwordValue));
- }
-}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.java b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.java
index 3857514..5e93c0f 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.java
@@ -26,6 +26,7 @@ import javax.jdo.annotations.PersistenceCapable;
import org.apache.isis.applib.annotation.Action;
import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.CommandReification;
import org.apache.isis.applib.annotation.DomainObject;
import org.apache.isis.applib.annotation.Editing;
import org.apache.isis.applib.annotation.MemberOrder;
@@ -56,9 +57,9 @@ public class ActionCommandJdo
public ActionCommandJdo(String initialValue) {
this.property = initialValue;
+ this.propertyCommandDisabled = initialValue;
this.propertyMetaAnnotated = initialValue;
this.propertyMetaAnnotatedOverridden = initialValue;
- this.propertyUpdateAsync = initialValue;
}
public String title() {
@@ -72,6 +73,11 @@ public class ActionCommandJdo
private String property;
@Property()
+ @MemberOrder(name = "annotation", sequence = "2")
+ @Getter @Setter
+ private String propertyCommandDisabled;
+
+ @Property()
@MemberOrder(name = "meta-annotated", sequence = "1")
@Getter @Setter
private String propertyMetaAnnotated;
@@ -80,16 +86,12 @@ public class ActionCommandJdo
@MemberOrder(name = "meta-annotated-overridden", sequence = "1")
@Getter @Setter
private String propertyMetaAnnotatedOverridden;
-
- @Property()
- @MemberOrder(name = "async", sequence = "1")
- @Getter @Setter
- private String propertyUpdateAsync;
//end::property[]
//tag::annotation[]
@Action(
- semantics = SemanticsOf.IDEMPOTENT
+ command = CommandReification.ENABLED
+ , semantics = SemanticsOf.IDEMPOTENT
, associateWith = "property"
, associateWithSequence = "1"
)
@@ -106,6 +108,26 @@ public class ActionCommandJdo
}
//end::annotation[]
+//tag::annotation-2[]
+ @Action(
+ command = CommandReification.DISABLED
+ , semantics = SemanticsOf.IDEMPOTENT
+ , associateWith = "propertyCommandDisabled"
+ , associateWithSequence = "1"
+ )
+ @ActionLayout(
+ named = "Update Property"
+ , describedAs = "@Action(command = ENABLED)"
+ )
+ public ActionCommandJdo updatePropertyCommandDisabledUsingAnnotation(final String value) {
+ setPropertyCommandDisabled(value);
+ return this;
+ }
+ public String default0UpdatePropertyCommandDisabledUsingAnnotation() {
+ return getPropertyCommandDisabled();
+ }
+//end::annotation[]
+
//tag::meta-annotation[]
@ActionCommandEnabledMetaAnnotation // <.>
@Action(
@@ -130,13 +152,14 @@ public class ActionCommandJdo
@ActionCommandDisabledMetaAnnotation // <.>
@Action(
semantics = SemanticsOf.IDEMPOTENT
+ , command = CommandReification.ENABLED
, associateWith = "propertyMetaAnnotatedOverridden"
, associateWithSequence = "1"
)
@ActionLayout(
named = "Update Property"
, describedAs =
- "@ActionCommandDisabledMetaAnnotation @Action(publishing = ENABLED)"
+ "@ActionCommandDisabledMetaAnnotation @Action(command = ENABLED)"
)
public ActionCommandJdo updatePropertyUsingMetaAnnotationButOverridden(final String value) {
setPropertyMetaAnnotatedOverridden(value);
@@ -147,28 +170,6 @@ public class ActionCommandJdo
}
//end::meta-annotation-overridden[]
-//tag::async[]
- @Action(
- semantics = SemanticsOf.IDEMPOTENT
- , associateWith = "propertyUpdateAsync"
- , associateWithSequence = "1"
- )
- @ActionLayout(
- describedAs = "@Action()"
- )
- public ActionCommandJdo updatePropertyAsync(final String value) {
- AsyncControl<Void> control = AsyncControl.control();
- ActionCommandJdo actionCommandJdo = this.wrapperFactory.asyncWrap(this, control);
- actionCommandJdo.setPropertyUpdateAsync(value);
- return this;
- }
- public String default0UpdatePropertyAsync() {
- return getPropertyUpdateAsync();
- }
-
- @Inject WrapperFactory wrapperFactory;
-//end::async[]
-
//tag::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.layout.xml
index 757b050..9207772 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.layout.xml
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.layout.xml
@@ -17,29 +17,10 @@
<bs3:row>
<bs3:col span="6">
- <bs3:row>
- <bs3:col span="6">
- <bs3:row>
- <bs3:col span="12">
- <cpt:fieldSet name="Annotated" id="annotation"/>
- <cpt:fieldSet name="Async" id="async"/>
- </bs3:col>
- </bs3:row>
- </bs3:col>
- <bs3:col span="6">
- <bs3:row>
- <bs3:col span="12">
- <cpt:fieldSet name="Meta-annotated" id="meta-annotated"/>
- <cpt:fieldSet name="Meta-annotated Overridden" id="meta-annotated-overridden"/>
- </bs3:col>
- </bs3:row>
- </bs3:col>
- </bs3:row>
- <bs3:row>
- <bs3:col span="12">
- <cpt:fieldSet name="Other" id="other" unreferencedProperties="true"/>
- </bs3:col>
- </bs3:row>
+ <cpt:fieldSet name="Annotated" id="annotation"/>
+ <cpt:fieldSet name="Meta-annotated" id="meta-annotated"/>
+ <cpt:fieldSet name="Meta-annotated Overridden" id="meta-annotated-overridden"/>
+ <cpt:fieldSet name="Other" id="other" unreferencedProperties="true"/>
</bs3:col>
<bs3:col span="6">
<cpt:fieldSet name="Description" id="description" >
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdateProperty.java b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdateProperty.java
index 349b825..806add4 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdateProperty.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdateProperty.java
@@ -2,19 +2,20 @@ package demoapp.dom.annotDomain.Action.command;
import org.apache.isis.applib.annotation.Action;
import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.CommandReification;
import org.apache.isis.applib.annotation.Publishing;
import org.apache.isis.applib.annotation.SemanticsOf;
//tag::class[]
@Action(
- publishing = Publishing.ENABLED // <.>
+ command = CommandReification.ENABLED
, semantics = SemanticsOf.IDEMPOTENT
, associateWith = "property"
, associateWithSequence = "2"
)
@ActionLayout(
named = "Mixin Update Property"
- , describedAs = "@Action(publishing = ENABLED)"
+ , describedAs = "@Action(command = ENABLED)"
)
public class ActionCommandJdo_mixinUpdateProperty {
// ...
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdateProperty.java b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdatePropertyCommandDisabled.java
similarity index 67%
copy from examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdateProperty.java
copy to examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdatePropertyCommandDisabled.java
index 349b825..3d84891 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdateProperty.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdatePropertyCommandDisabled.java
@@ -2,27 +2,27 @@ package demoapp.dom.annotDomain.Action.command;
import org.apache.isis.applib.annotation.Action;
import org.apache.isis.applib.annotation.ActionLayout;
-import org.apache.isis.applib.annotation.Publishing;
+import org.apache.isis.applib.annotation.CommandReification;
import org.apache.isis.applib.annotation.SemanticsOf;
//tag::class[]
@Action(
- publishing = Publishing.ENABLED // <.>
+ command = CommandReification.DISABLED
, semantics = SemanticsOf.IDEMPOTENT
- , associateWith = "property"
+ , associateWith = "propertyCommandDisabled"
, associateWithSequence = "2"
)
@ActionLayout(
named = "Mixin Update Property"
- , describedAs = "@Action(publishing = ENABLED)"
+ , describedAs = "@Action(command = DISABLED)"
)
-public class ActionCommandJdo_mixinUpdateProperty {
+public class ActionCommandJdo_mixinUpdatePropertyCommandDisabled {
// ...
//end::class[]
private final ActionCommandJdo actionCommandJdo;
- public ActionCommandJdo_mixinUpdateProperty(ActionCommandJdo actionCommandJdo) {
+ public ActionCommandJdo_mixinUpdatePropertyCommandDisabled(ActionCommandJdo actionCommandJdo) {
this.actionCommandJdo = actionCommandJdo;
}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdatePropertyMetaAnnotation.java b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdatePropertyMetaAnnotation.java
index 5d3e7e7..5adeda7 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdatePropertyMetaAnnotation.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdatePropertyMetaAnnotation.java
@@ -4,6 +4,8 @@ import org.apache.isis.applib.annotation.Action;
import org.apache.isis.applib.annotation.ActionLayout;
import org.apache.isis.applib.annotation.SemanticsOf;
+import lombok.RequiredArgsConstructor;
+
//tag::class[]
@ActionCommandEnabledMetaAnnotation // <.>
@Action(
@@ -15,17 +17,11 @@ import org.apache.isis.applib.annotation.SemanticsOf;
named = "Mixin Update Property"
, describedAs = "@ActionPublishingEnabledMetaAnnotation"
)
+@RequiredArgsConstructor
public class ActionCommandJdo_mixinUpdatePropertyMetaAnnotation {
- // ...
-//end::class[]
private final ActionCommandJdo actionCommandJdo;
- public ActionCommandJdo_mixinUpdatePropertyMetaAnnotation(ActionCommandJdo actionCommandJdo) {
- this.actionCommandJdo = actionCommandJdo;
- }
-
-//tag::class[]
public ActionCommandJdo act(final String value) {
actionCommandJdo.setPropertyMetaAnnotated(value);
return actionCommandJdo;
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdatePropertyMetaAnnotationOverridden.java b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdatePropertyMetaAnnotationOverridden.java
index 52f6ac7..6f42ebf 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdatePropertyMetaAnnotationOverridden.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo_mixinUpdatePropertyMetaAnnotationOverridden.java
@@ -2,13 +2,14 @@ package demoapp.dom.annotDomain.Action.command;
import org.apache.isis.applib.annotation.Action;
import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.CommandReification;
import org.apache.isis.applib.annotation.Publishing;
import org.apache.isis.applib.annotation.SemanticsOf;
//tag::class[]
-@ActionCommandDisabledMetaAnnotation // <.>
+@ActionCommandDisabledMetaAnnotation // <.>
@Action(
- publishing = Publishing.ENABLED // <.>
+ command = CommandReification.ENABLED // <.>
, semantics = SemanticsOf.IDEMPOTENT
, associateWith = "propertyMetaAnnotatedOverridden"
, associateWithSequence = "2"
@@ -16,8 +17,8 @@ import org.apache.isis.applib.annotation.SemanticsOf;
@ActionLayout(
named = "Mixin Update Property"
, describedAs =
- "@ActionPublishingDisabledMetaAnnotation " +
- "@Action(publishing = ENABLED)"
+ "@ActionCommandDisabledMetaAnnotation " +
+ "@Action(command = ENABLED)"
)
public class ActionCommandJdo_mixinUpdatePropertyMetaAnnotationOverridden {
// ...
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/events/DemoEventSubscriber.java b/examples/demo/domain/src/main/java/demoapp/dom/events/DemoEventSubscriber.java
index 0a7223d..7b84a69 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/events/DemoEventSubscriber.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/events/DemoEventSubscriber.java
@@ -55,7 +55,7 @@ public class DemoEventSubscriber {
val eventLogWriter = factoryService.get(EventLogWriter.class); // <-- get a new writer
- wrapper.asyncWrap(eventLogWriter, AsyncControl.control()).storeEvent(event);
+ wrapper.asyncWrap(eventLogWriter, AsyncControl.returningVoid()).storeEvent(event);
}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
index b37889b..2f08098 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
+++ b/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
@@ -10,6 +10,7 @@ as a replacement for
xmlns:cpt="http://isis.apache.org/applib/layout/component"
xmlns:mb3="http://isis.apache.org/applib/layout/menubars/bootstrap3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+
<mb3:primary>
<mb3:menu>
<mb3:named>Basic Types</mb3:named>
@@ -105,7 +106,7 @@ as a replacement for
</mb3:menu>
<mb3:menu>
- <mb3:named>Domain Annotations</mb3:named>
+ <mb3:named>Domain Annot</mb3:named>
<mb3:section>
<mb3:named>@DomainObject</mb3:named>
<mb3:serviceAction objectType="demo.DomainObjectMenu" id="publishing"/>
@@ -134,7 +135,7 @@ as a replacement for
</mb3:menu>
<mb3:menu>
- <mb3:named>Layout Annotations</mb3:named>
+ <mb3:named>Layout Annot</mb3:named>
<mb3:section>
<mb3:named>@ActionLayout</mb3:named>
<mb3:serviceAction objectType="demo.ActionLayoutMenu" id="position"/>
@@ -156,6 +157,13 @@ as a replacement for
</mb3:menu>
<mb3:menu>
+ <mb3:named>Services</mb3:named>
+ <mb3:section>
+ <mb3:serviceAction objectType="demo.ServicesMenu" id="wrapperFactory"/>
+ </mb3:section>
+ </mb3:menu>
+
+ <mb3:menu>
<mb3:named>View Models</mb3:named>
<mb3:section>
<mb3:serviceAction objectType="demo.ViewModelMenu" id="stateful"/>
@@ -388,6 +396,7 @@ as a replacement for
</mb3:menu>
</mb3:secondary>
+
<mb3:tertiary>
<mb3:menu>
<mb3:named/>
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/services/ServicesMenu.java b/examples/demo/domain/src/main/java/demoapp/dom/services/ServicesMenu.java
new file mode 100644
index 0000000..7163f41
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/services/ServicesMenu.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package demoapp.dom.services;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.SemanticsOf;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+
+import demoapp.dom.services.wrapperFactory.WrapperFactoryJdo;
+import demoapp.dom.services.wrapperFactory.WrapperFactoryJdoEntities;
+
+@DomainService(nature=NatureOfService.VIEW, objectType = "demo.ServicesMenu")
+@Log4j2
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+public class ServicesMenu {
+
+ final WrapperFactoryJdoEntities wrapperFactoryJdoEntities;
+
+ @Action(semantics = SemanticsOf.SAFE)
+ @ActionLayout(cssClassFa="fa-gift", describedAs = "Formal object interactions + async")
+ public WrapperFactoryJdo wrapperFactory(){
+ return wrapperFactoryJdoEntities.first();
+ }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo-description.adoc
new file mode 100644
index 0000000..7c13ad0
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo-description.adoc
@@ -0,0 +1,2 @@
+CAUTION: TODO - to document
+
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo.java b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo.java
new file mode 100644
index 0000000..63d1181
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package demoapp.dom.services.wrapperFactory;
+
+import javax.inject.Inject;
+import javax.jdo.annotations.DatastoreIdentity;
+import javax.jdo.annotations.IdGeneratorStrategy;
+import javax.jdo.annotations.IdentityType;
+import javax.jdo.annotations.PersistenceCapable;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.Nature;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.services.factory.FactoryService;
+import org.apache.isis.applib.services.wrapper.WrapperFactory;
+import org.apache.isis.applib.services.wrapper.control.AsyncControl;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.val;
+
+import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+import demoapp.dom.annotDomain._commands.ExposePersistedCommands;
+
+//tag::class[]
+@PersistenceCapable(identityType = IdentityType.DATASTORE, schema = "demo")
+@DatastoreIdentity(strategy = IdGeneratorStrategy.IDENTITY, column = "id")
+@DomainObject(
+ nature=Nature.JDO_ENTITY
+ , objectType = "demo.WrapperFactoryJdo"
+ , editing = Editing.DISABLED
+)
+public class WrapperFactoryJdo
+ implements HasAsciiDocDescription, ExposePersistedCommands {
+
+ @Inject WrapperFactory wrapperFactory;
+ @Inject FactoryService factoryService;
+
+ // ...
+//end::class[]
+
+ public WrapperFactoryJdo(String initialValue) {
+ this.propertyAsync = initialValue;
+ this.propertyAsyncMixin = initialValue;
+ }
+
+ public String title() {
+ return "WrapperFactory";
+ }
+
+//tag::property[]
+ @Property()
+ @MemberOrder(name = "async", sequence = "1")
+ @Getter @Setter
+ private String propertyAsync;
+
+ @Property()
+ @MemberOrder(name = "async", sequence = "2")
+ @Getter @Setter
+ private String propertyAsyncMixin;
+//end::property[]
+
+//tag::async[]
+ @Action(
+ semantics = SemanticsOf.IDEMPOTENT
+ , associateWith = "propertyAsync"
+ , associateWithSequence = "1"
+ )
+ @ActionLayout(
+ describedAs = "@Action()"
+ )
+ public WrapperFactoryJdo updatePropertyAsync(final String value) {
+ val control = AsyncControl.returningVoid().withSkipRules();
+ val wrapperFactoryJdo = this.wrapperFactory.asyncWrap(this, control);
+ wrapperFactoryJdo.setPropertyAsync(value);
+ return this;
+ }
+ public String default0UpdatePropertyAsync() {
+ return getPropertyAsync();
+ }
+//end::async[]
+
+//tag::async[]
+ @Action(
+ semantics = SemanticsOf.IDEMPOTENT
+ , associateWith = "propertyAsyncMixin"
+ , associateWithSequence = "1"
+ )
+ @ActionLayout(
+ describedAs = "Calls the 'updatePropertyAsync' (mixin) action asynchronously"
+ )
+ public WrapperFactoryJdo updatePropertyUsingAsyncWrapMixin(final String value) {
+ val control = AsyncControl.returning(WrapperFactoryJdo.class).withSkipRules();
+ val mixin = this.wrapperFactory.asyncWrapMixin(WrapperFactoryJdo_updatePropertyAsyncMixin.class, this, control);
+ WrapperFactoryJdo act = mixin.act(value);
+ return this;
+ }
+ public String default0UpdatePropertyUsingAsyncWrapMixin() {
+ return new WrapperFactoryJdo_updatePropertyAsyncMixin(this).default0Act();
+ }
+//end::async[]
+
+//tag::class[]
+
+}
+//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo.layout.xml
similarity index 66%
copy from examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.layout.xml
copy to examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo.layout.xml
index 757b050..f83f3d4 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.layout.xml
+++ b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo.layout.xml
@@ -17,29 +17,8 @@
<bs3:row>
<bs3:col span="6">
- <bs3:row>
- <bs3:col span="6">
- <bs3:row>
- <bs3:col span="12">
- <cpt:fieldSet name="Annotated" id="annotation"/>
- <cpt:fieldSet name="Async" id="async"/>
- </bs3:col>
- </bs3:row>
- </bs3:col>
- <bs3:col span="6">
- <bs3:row>
- <bs3:col span="12">
- <cpt:fieldSet name="Meta-annotated" id="meta-annotated"/>
- <cpt:fieldSet name="Meta-annotated Overridden" id="meta-annotated-overridden"/>
- </bs3:col>
- </bs3:row>
- </bs3:col>
- </bs3:row>
- <bs3:row>
- <bs3:col span="12">
- <cpt:fieldSet name="Other" id="other" unreferencedProperties="true"/>
- </bs3:col>
- </bs3:row>
+ <cpt:fieldSet name="Async" id="async"/>
+ <cpt:fieldSet name="Other" id="other" unreferencedProperties="true"/>
</bs3:col>
<bs3:col span="6">
<cpt:fieldSet name="Description" id="description" >
@@ -55,11 +34,6 @@
</bs3:col>
</bs3:row>
<bs3:row>
- <bs3:col span="12">
- <cpt:collection id="commands"/>
- </bs3:col>
- </bs3:row>
- <bs3:row>
<bs3:col span="12" unreferencedCollections="true"/>
</bs3:row>
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdoEntities.java b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdoEntities.java
new file mode 100644
index 0000000..ae414e3
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdoEntities.java
@@ -0,0 +1,33 @@
+package demoapp.dom.services.wrapperFactory;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.services.repository.RepositoryService;
+
+import lombok.RequiredArgsConstructor;
+
+@Service
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+public class WrapperFactoryJdoEntities {
+
+ final RepositoryService repositoryService;
+
+ public Optional<WrapperFactoryJdo> find(final String value) {
+ return repositoryService.firstMatch(WrapperFactoryJdo.class, x -> Objects.equals(x.getPropertyAsync(), value));
+ }
+
+ public List<WrapperFactoryJdo> all() {
+ return repositoryService.allInstances(WrapperFactoryJdo.class);
+ }
+
+ public WrapperFactoryJdo first() {
+ return all().stream().findFirst().get();
+ }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdoSeedService.java b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdoSeedService.java
new file mode 100644
index 0000000..3389e0e
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdoSeedService.java
@@ -0,0 +1,39 @@
+package demoapp.dom.services.wrapperFactory;
+
+import javax.inject.Inject;
+
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
+
+import demoapp.dom._infra.seed.SeedServiceAbstract;
+import demoapp.dom.types.Samples;
+
+@Service
+public class WrapperFactoryJdoSeedService extends SeedServiceAbstract {
+
+ public WrapperFactoryJdoSeedService() {
+ super(PropertyPublishingJdoEntityFixture::new);
+ }
+
+ static class PropertyPublishingJdoEntityFixture extends FixtureScript {
+
+ @Override
+ protected void execute(ExecutionContext executionContext) {
+ samples.stream()
+ .map(WrapperFactoryJdo::new)
+ .forEach(domainObject -> {
+ repositoryService.persist(domainObject);
+ executionContext.addResult(this, domainObject);
+ });
+
+ }
+
+ @Inject
+ RepositoryService repositoryService;
+
+ @Inject
+ Samples<String> samples;
+ }
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo_mixinUpdatePropertyAsync.java b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo_mixinUpdatePropertyAsync.java
new file mode 100644
index 0000000..626ddcf
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo_mixinUpdatePropertyAsync.java
@@ -0,0 +1,44 @@
+package demoapp.dom.services.wrapperFactory;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.services.wrapper.WrapperFactory;
+import org.apache.isis.applib.services.wrapper.control.AsyncControl;
+
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+
+//tag::class[]
+@Action(
+ semantics = SemanticsOf.IDEMPOTENT
+ , associateWith = "propertyAsync"
+ , associateWithSequence = "2"
+)
+@ActionLayout(
+ named = "Mixin Update Property"
+)
+@RequiredArgsConstructor
+public class WrapperFactoryJdo_mixinUpdatePropertyAsync {
+
+ @Inject WrapperFactory wrapperFactory;
+
+ // ...
+//end::class[]
+
+ private final WrapperFactoryJdo wrapperFactoryJdo;
+
+//tag::class[]
+ public WrapperFactoryJdo act(final String value) {
+ val control = AsyncControl.returningVoid().withSkipRules();
+ val wrapped = this.wrapperFactory.asyncWrap(this.wrapperFactoryJdo, control);
+ wrapped.setPropertyAsync(value);
+ return this.wrapperFactoryJdo;
+ }
+ public String default0Act() {
+ return wrapperFactoryJdo.getPropertyAsync();
+ }
+}
+//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo_updatePropertyAsyncMixin.java b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo_updatePropertyAsyncMixin.java
new file mode 100644
index 0000000..472b5d2
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/services/wrapperFactory/WrapperFactoryJdo_updatePropertyAsyncMixin.java
@@ -0,0 +1,40 @@
+package demoapp.dom.services.wrapperFactory;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.services.wrapper.WrapperFactory;
+import org.apache.isis.applib.services.wrapper.control.AsyncControl;
+
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+
+//tag::class[]
+@Action(
+ semantics = SemanticsOf.IDEMPOTENT
+ , associateWith = "propertyAsyncMixin"
+ , associateWithSequence = "2"
+)
+@ActionLayout(
+ named = "Update Property Async"
+ , describedAs = "Mixin that Updates 'property async mixin' directly"
+)
+@RequiredArgsConstructor
+public class WrapperFactoryJdo_updatePropertyAsyncMixin {
+ // ...
+//end::class[]
+
+ private final WrapperFactoryJdo wrapperFactoryJdo;
+
+//tag::class[]
+ public WrapperFactoryJdo act(final String value) {
+ wrapperFactoryJdo.setPropertyAsyncMixin(value);
+ return wrapperFactoryJdo;
+ }
+ public String default0Act() {
+ return wrapperFactoryJdo.getPropertyAsyncMixin();
+ }
+}
+//end::class[]
diff --git a/examples/smoketests/stable/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java b/examples/smoketests/stable/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java
index 9f3c21b..f48a11c 100644
--- a/examples/smoketests/stable/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java
+++ b/examples/smoketests/stable/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java
@@ -39,7 +39,6 @@ import org.springframework.transaction.TransactionStatus;
import org.apache.isis.applib.annotation.OrderPrecedence;
import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandContext;
import org.apache.isis.applib.services.command.CommandService;
import org.apache.isis.applib.services.iactn.Interaction;
@@ -112,7 +111,6 @@ public class Configuration_headless {
public static class HeadlessCommandSupport {
private final Provider<InteractionContext> interactionContextProvider;
- private final Provider<CommandContext> commandContextProvider;
private final CommandService commandService;
@@ -133,21 +131,13 @@ public class Configuration_headless {
public void setupCommandCreateIfMissing() {
- val commandContext = commandContextProvider.get();
- final Command command = Optional.ofNullable(commandContext.getCommand())
- .orElseGet(()->{
- val newCommand = commandService.create();
- commandContext.setCommand(newCommand);
- return newCommand;
- });
-
val interactionContext = interactionContextProvider.get();
@SuppressWarnings("unused")
final Interaction interaction = Optional.ofNullable(interactionContext.getInteraction())
.orElseGet(()->{
- val newInteraction = new Interaction();
+ val newCommand = new Command();
+ val newInteraction = new Interaction(newCommand);
interactionContext.setInteraction(newInteraction);
- newInteraction.setUniqueId(command.getUniqueId());
return newInteraction;
});
}
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/CommandServiceListenerForJdo.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/CommandServiceListenerForJdo.java
index f3a4d81..a6b3d8c 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/CommandServiceListenerForJdo.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/CommandServiceListenerForJdo.java
@@ -18,6 +18,8 @@
*/
package org.apache.isis.extensions.commandlog.impl;
+import java.util.Optional;
+
import javax.inject.Inject;
import javax.inject.Named;
@@ -28,8 +30,10 @@ import org.springframework.stereotype.Service;
import org.apache.isis.applib.annotation.OrderPrecedence;
import org.apache.isis.applib.services.command.Command;
import org.apache.isis.applib.services.command.spi.CommandServiceListener;
+import org.apache.isis.applib.util.JaxbUtil;
import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
+import org.apache.isis.schema.cmd.v2.CommandDto;
import lombok.RequiredArgsConstructor;
import lombok.val;
@@ -52,17 +56,32 @@ public class CommandServiceListenerForJdo implements CommandServiceListener {
return;
}
- val commandJdo = new CommandJdo(command);
- val parent = command.getParent();
- val parentJdo =
- parent != null
- ? commandJdoRepository
- .findByUniqueId(parent.getUniqueId())
- .orElse(null)
- : null;
- commandJdo.setParent(parentJdo);
+ val existingCommandJdoIfAny =
+ commandJdoRepository.findByUniqueId(command.getUniqueId());
+ if(existingCommandJdoIfAny.isPresent()) {
+ if(true || log.isDebugEnabled()) {
+ val existingCommandDto = existingCommandJdoIfAny.get().getCommandDto();
+
+ String existingCommandDtoXml = JaxbUtil.toXml(existingCommandDto);
+ String commandDtoXml = JaxbUtil.toXml(command.getCommandDto());
+
+ log.debug("existing: \n" + existingCommandDtoXml);
+ log.debug("proposed: \n" + commandDtoXml);
+ }
+ } else {
+ val commandJdo = new CommandJdo(command);
+ val parent = command.getParent();
+ val parentJdo =
+ parent != null
+ ? commandJdoRepository
+ .findByUniqueId(parent.getUniqueId())
+ .orElse(null)
+ : null;
+ commandJdo.setParent(parentJdo);
+ commandJdoRepository.persist(commandJdo);
+ }
+
- commandJdoRepository.persist(commandJdo);
}
}
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdoRepository.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdoRepository.java
index cee771a..2b3450f 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdoRepository.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdoRepository.java
@@ -104,7 +104,6 @@ public class CommandJdoRepository {
public Optional<CommandJdo> findByUniqueId(final UUID uniqueId) {
- persistCurrentCommandIfRequired();
return repositoryService.firstMatch(
new QueryDefault<>(CommandJdo.class,
"findByUniqueIdStr",
@@ -120,35 +119,17 @@ public class CommandJdoRepository {
public List<CommandJdo> findCurrent() {
- persistCurrentCommandIfRequired();
return repositoryService.allMatches(
new QueryDefault<>(CommandJdo.class, "findCurrent"));
}
public List<CommandJdo> findCompleted() {
- persistCurrentCommandIfRequired();
return repositoryService.allMatches(
new QueryDefault<>(CommandJdo.class, "findCompleted"));
}
- private void persistCurrentCommandIfRequired() {
- if(interactionContextProvider == null) {
- // hmm, this shouldn't happen..
- return;
- }
-
- val command = interactionContextProvider.get().getInteraction().getCommand();
- val systemStateChanged = command.isSystemStateChanged();
- if(!systemStateChanged) {
- return;
- }
- final CommandJdo commandJdo = new CommandJdo(command);
- repositoryService.persist(commandJdo);
- }
-
-
public List<CommandJdo> findByTargetAndFromAndTo(
final Bookmark target
, final LocalDate from
@@ -381,6 +362,8 @@ public class CommandJdoRepository {
repositoryService.persist(commandJdo);
}
-
+ public void truncateLog() {
+ repositoryService.removeAll(CommandJdo.class);
+ }
}
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/ui/CommandServiceMenu.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/ui/CommandServiceMenu.java
index ce48ad7..4e36848 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/ui/CommandServiceMenu.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/ui/CommandServiceMenu.java
@@ -22,12 +22,15 @@ import org.apache.isis.applib.annotation.Optionality;
import org.apache.isis.applib.annotation.OrderPrecedence;
import org.apache.isis.applib.annotation.Parameter;
import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.RestrictTo;
import org.apache.isis.applib.annotation.SemanticsOf;
import org.apache.isis.applib.services.clock.ClockService;
import org.apache.isis.extensions.commandlog.impl.IsisModuleExtCommandLogImpl;
import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
+import lombok.RequiredArgsConstructor;
+
@DomainService(
nature = NatureOfService.VIEW,
objectType = "isisExtensionsCommandLog.CommandServiceMenu"
@@ -40,6 +43,7 @@ import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
@Named("isisExtensionsCommandLog.CommandServiceMenu")
@Order(OrderPrecedence.MIDPOINT)
@Qualifier("Jdo")
+@RequiredArgsConstructor(onConstructor_ = { @Inject })
public class CommandServiceMenu {
public static abstract class PropertyDomainEvent<T>
@@ -50,6 +54,8 @@ public class CommandServiceMenu {
extends IsisModuleExtCommandLogImpl.ActionDomainEvent<CommandServiceMenu> {
}
+ final CommandJdoRepository commandServiceRepository;
+ final ClockService clockService;
public static class ActiveCommandsDomainEvent extends ActionDomainEvent { }
@Action(domainEvent = ActiveCommandsDomainEvent.class, semantics = SemanticsOf.SAFE)
@@ -101,7 +107,14 @@ public class CommandServiceMenu {
}
- @Inject CommandJdoRepository commandServiceRepository;
- @Inject ClockService clockService;
+ public static class TruncateLogDomainEvent extends ActionDomainEvent { }
+ @Action(domainEvent = TruncateLogDomainEvent.class, semantics = SemanticsOf.IDEMPOTENT_ARE_YOU_SURE, restrictTo = RestrictTo.PROTOTYPING)
+ @ActionLayout(cssClassFa = "fa-trash")
+ @MemberOrder(sequence="40")
+ public void truncateLog() {
+ commandServiceRepository.truncateLog();
+ }
+
+
}
diff --git a/extensions/core/command-replay/secondary/src/test/java/org/apache/isis/extensions/commandreplay/secondary/fetch/CommandFetcher_Test.java b/extensions/core/command-replay/secondary/src/test/java/org/apache/isis/extensions/commandreplay/secondary/fetch/CommandFetcher_Test.java
index 4fbc55b..eae6546 100644
--- a/extensions/core/command-replay/secondary/src/test/java/org/apache/isis/extensions/commandreplay/secondary/fetch/CommandFetcher_Test.java
+++ b/extensions/core/command-replay/secondary/src/test/java/org/apache/isis/extensions/commandreplay/secondary/fetch/CommandFetcher_Test.java
@@ -4,6 +4,7 @@ import java.net.URI;
import javax.ws.rs.core.UriBuilder;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.apache.isis.applib.services.jaxb.JaxbService;
@@ -17,6 +18,7 @@ import lombok.val;
public class CommandFetcher_Test {
+ @Disabled // intended only for manual verification.
@Test
public void testing_the_unmarshalling() {
val jaxRsClient = new JaxRsClientDefault();
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/auth/VaadinAuthenticationHandler.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/auth/VaadinAuthenticationHandler.java
index d35a3d5..f07ae72 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/auth/VaadinAuthenticationHandler.java
+++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/auth/VaadinAuthenticationHandler.java
@@ -111,7 +111,7 @@ public class VaadinAuthenticationHandler implements VaadinServiceInitListener {
val authSession = AuthSessionStoreUtil.get().orElse(null);
if(authSession!=null) {
- isisInteractionFactory.openSession(authSession);
+ isisInteractionFactory.openInteraction(authSession);
return; // access granted
}
// otherwise redirect to login page
diff --git a/legacy/extensions/core/applib/src/main/java/org/apache/isis/legacy/applib/services/eventbus/ActionDomainEvent.java b/legacy/extensions/core/applib/src/main/java/org/apache/isis/legacy/applib/services/eventbus/ActionDomainEvent.java
index 3714f9e..47ff80c 100644
--- a/legacy/extensions/core/applib/src/main/java/org/apache/isis/legacy/applib/services/eventbus/ActionDomainEvent.java
+++ b/legacy/extensions/core/applib/src/main/java/org/apache/isis/legacy/applib/services/eventbus/ActionDomainEvent.java
@@ -24,7 +24,7 @@ import java.util.List;
import org.apache.isis.applib.annotation.SemanticsOf;
import org.apache.isis.applib.events.domain.AbstractDomainEvent;
import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandContext;
+import org.apache.isis.applib.services.iactn.Interaction;
import org.apache.isis.applib.util.ObjectContracts;
import org.apache.isis.applib.util.ToString;
@@ -77,7 +77,7 @@ public abstract class ActionDomainEvent<S> extends AbstractDomainEvent<S> {
private Command command;
/**
- * @deprecated - use {@link CommandContext#getCommand()} to obtain the current {@link Command}.
+ * @deprecated - use {@link Interaction#getCommand()} to obtain the current {@link Command}.
*/
@Deprecated
public Command getCommand() {
@@ -87,7 +87,7 @@ public abstract class ActionDomainEvent<S> extends AbstractDomainEvent<S> {
/**
* Not API - set by the framework.
*
- * @deprecated - the corresponding {@link #getCommand()} should not be called, instead use {@link CommandContext#getCommand()} to obtain the current {@link Command}.
+ * @deprecated - the corresponding {@link #getCommand()} should not be called, instead use {@link Interaction#getCommand()} to obtain the current {@link Command}.
*/
@Deprecated
public void setCommand(Command command) {
diff --git a/testing/integtestsupport/adoc/modules/integtestsupport/pages/about.adoc b/testing/integtestsupport/adoc/modules/integtestsupport/pages/about.adoc
index 49a49a8..cd400d4 100644
--- a/testing/integtestsupport/adoc/modules/integtestsupport/pages/about.adoc
+++ b/testing/integtestsupport/adoc/modules/integtestsupport/pages/about.adoc
@@ -380,7 +380,7 @@ This is discussed further xref:about.adoc#wrapper-factory[below].
for more control over transactions
-The class also defines (as a nested static class) the `CommandSupport` as a domain service.
+The class also defines (as a nested static class) the `InteractionSupport` as a domain service.
If ``@Import``ed into the integration test's "app manifest", this ensures that any ``Command``s that are raised as the result of interactions through the wrapper factory are setup correctly.
diff --git a/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/IsisIntegrationTestAbstract.java b/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/IsisIntegrationTestAbstract.java
index 33c8535..3ae6cda 100644
--- a/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/IsisIntegrationTestAbstract.java
+++ b/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/IsisIntegrationTestAbstract.java
@@ -26,12 +26,10 @@ import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
-import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.CommandReification;
import org.apache.isis.applib.annotation.OrderPrecedence;
import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandContext;
import org.apache.isis.applib.services.factory.FactoryService;
+import org.apache.isis.applib.services.iactn.InteractionContext;
import org.apache.isis.applib.services.metamodel.MetaModelService;
import org.apache.isis.applib.services.registry.ServiceRegistry;
import org.apache.isis.applib.services.repository.RepositoryService;
@@ -43,7 +41,6 @@ import org.apache.isis.core.runtime.persistence.transaction.events.TransactionAf
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
-import lombok.val;
/**
* Convenient base class to extend for integration tests.
@@ -54,14 +51,16 @@ import lombok.val;
public abstract class IsisIntegrationTestAbstract {
/**
- * Hook to interact with {@link Command}s (currently unused).
+ * Hook to interact with
+ * {@link org.apache.isis.applib.services.iactn.Interaction}s and
+ * therefore also {@link Command}s (currently unused).
*/
@Service
@Order(OrderPrecedence.MIDPOINT)
@RequiredArgsConstructor(onConstructor_ = {@Inject})
- public static class CommandSupport {
+ public static class InteractionSupport {
- private final Provider<CommandContext> commandContextProvider;
+ private final Provider<InteractionContext> interactionContextProvider;
@EventListener
public void on(final TransactionAfterBeginEvent event) {
diff --git a/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/IsisInteractionHandler.java b/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/IsisInteractionHandler.java
index 02f3a4b..17fa8e8 100644
--- a/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/IsisInteractionHandler.java
+++ b/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/IsisInteractionHandler.java
@@ -32,7 +32,7 @@ public class IsisInteractionHandler implements BeforeEachCallback, AfterEachCall
@Override
public void beforeEach(ExtensionContext extensionContext) throws Exception {
isisInteractionFactory(extensionContext)
- .ifPresent(isisInteractionFactory->isisInteractionFactory.openSession(new InitialisationSession()));
+ .ifPresent(isisInteractionFactory->isisInteractionFactory.openInteraction(new InitialisationSession()));
}
@Override
diff --git a/tooling/javamodel/pom.xml b/tooling/javamodel/pom.xml
index 7e8ee26..d542f61 100644
--- a/tooling/javamodel/pom.xml
+++ b/tooling/javamodel/pom.xml
@@ -29,6 +29,7 @@
<properties>
<code-assert.version>0.9.13</code-assert.version>
+ <skipTests>true</skipTests>
</properties>
<dependencies>
diff --git a/tooling/projectmodel/pom.xml b/tooling/projectmodel/pom.xml
index 14b0f4c..efcbb0e 100644
--- a/tooling/projectmodel/pom.xml
+++ b/tooling/projectmodel/pom.xml
@@ -28,6 +28,7 @@
</description>
<properties>
+ <skipTests>true</skipTests>
</properties>
<dependencies>
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/JGrowlUtil.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/JGrowlUtil.java
index b358bbf..04c3fec 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/JGrowlUtil.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/JGrowlUtil.java
@@ -47,10 +47,10 @@ public class JGrowlUtil {
final StringBuilder buf = new StringBuilder();
for (String info : messageBroker.getMessages()) {
- addJGrowlCall(info, MessageSeverity.INFO, buf);
+ addJGrowlCall(info, JGrowlUtil.MessageSeverity.INFO, buf);
}
for (String warning : messageBroker.getWarnings()) {
- addJGrowlCall(warning, MessageSeverity.WARNING, buf);
+ addJGrowlCall(warning, JGrowlUtil.MessageSeverity.WARNING, buf);
}
final String error = messageBroker.getApplicationError();
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/FormExecutorDefault.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/FormExecutorDefault.java
index fcb048b..58e38ce 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/FormExecutorDefault.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/FormExecutorDefault.java
@@ -36,7 +36,6 @@ import org.apache.wicket.util.visit.IVisitor;
import org.apache.isis.applib.RecoverableException;
import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandContext;
import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer;
import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer.Category;
import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer.Recognition;
@@ -45,7 +44,6 @@ import org.apache.isis.applib.services.i18n.TranslationService;
import org.apache.isis.applib.services.message.MessageService;
import org.apache.isis.applib.services.registry.ServiceRegistry;
import org.apache.isis.core.commons.internal.collections._Sets;
-import org.apache.isis.core.commons.internal.exceptions._Exceptions;
import org.apache.isis.core.metamodel.facets.actions.redirect.RedirectFacet;
import org.apache.isis.core.metamodel.facets.properties.renderunchanged.UnchangingFacet;
import org.apache.isis.core.metamodel.spec.ManagedObject;
@@ -242,7 +240,7 @@ implements FormExecutor {
// irrespective, capture error in the Command, and propagate
if (command != null) {
- command.internal().setException(ex);
+ command.updater().setException(ex);
//XXX legacy of
//command.internal().setException(Throwables.getStackTraceAsString(ex));
@@ -487,11 +485,6 @@ implements FormExecutor {
return getCommonContext().lookupServiceElseFail(WicketViewerSettings.class);
}
- // request-scoped
- private Optional<CommandContext> currentCommandContext() {
- return getServiceRegistry().lookupService(CommandContext.class);
- }
-
///////////////////////////////////////////////////////////////////////////////
private ManagedObject obtainTargetAdapter() {