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 2016/05/01 13:16:49 UTC

[1/3] isis git commit: ISIS-1370: factored out AuditingServiceInternal as wrapper around AuditingService3 (similar design to PublshingServiceInternal wrapping PublishingService).

Repository: isis
Updated Branches:
  refs/heads/ISIS-1291 9d8847fc3 -> ad43cad3f


ISIS-1370: factored out AuditingServiceInternal as wrapper around AuditingService3 (similar design to PublshingServiceInternal wrapping PublishingService).


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/5870f6a5
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/5870f6a5
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/5870f6a5

Branch: refs/heads/ISIS-1291
Commit: 5870f6a5973fd65af8d054ffe872f24b80fbdefc
Parents: 9d8847f
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Sun May 1 11:22:50 2016 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Sun May 1 11:22:50 2016 +0100

----------------------------------------------------------------------
 .../publishing/AuditingServiceInternal.java     | 117 +++++++++++++++++++
 .../system/transaction/IsisTransaction.java     |  91 +++------------
 2 files changed, 135 insertions(+), 73 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/5870f6a5/core/runtime/src/main/java/org/apache/isis/core/runtime/services/publishing/AuditingServiceInternal.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/publishing/AuditingServiceInternal.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/publishing/AuditingServiceInternal.java
new file mode 100644
index 0000000..3f29804
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/publishing/AuditingServiceInternal.java
@@ -0,0 +1,117 @@
+/*
+ *  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.runtime.services.publishing;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.services.audit.AuditingService3;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.clock.ClockService;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.services.command.CommandContext;
+import org.apache.isis.applib.services.user.UserService;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facets.actions.action.invocation.CommandUtil;
+import org.apache.isis.core.metamodel.facets.object.audit.AuditableFacet;
+import org.apache.isis.core.runtime.system.transaction.IsisTransaction;
+
+/**
+ * Wrapper around {@link org.apache.isis.applib.services.audit.AuditingService3}.  Is a no-op if there is no injected service.
+ */
+@DomainService(nature = NatureOfService.DOMAIN)
+public class AuditingServiceInternal {
+
+
+    @Programmatic
+    public boolean canAudit() {
+        return auditingServiceIfAny != null;
+    }
+
+    @Programmatic
+    public void audit(
+            final Set<Map.Entry<IsisTransaction.AdapterAndProperty, IsisTransaction.PreAndPostValues>> changedObjectProperties) {
+
+        if(!canAudit()) {
+            return;
+        }
+
+        final String currentUser = userService.getUser().getName();
+        final java.sql.Timestamp currentTime = clockService.nowAsJavaSqlTimestamp();
+
+        for (Map.Entry<IsisTransaction.AdapterAndProperty, IsisTransaction.PreAndPostValues> auditEntry : changedObjectProperties) {
+            auditChangedProperty(currentTime, currentUser, auditEntry);
+        }
+
+    }
+
+    private void auditChangedProperty(
+            final java.sql.Timestamp timestamp,
+            final String user,
+            final Map.Entry<IsisTransaction.AdapterAndProperty, IsisTransaction.PreAndPostValues> auditEntry) {
+
+        final IsisTransaction.AdapterAndProperty aap = auditEntry.getKey();
+        final ObjectAdapter adapter = aap.getAdapter();
+
+        final AuditableFacet auditableFacet = adapter.getSpecification().getFacet(AuditableFacet.class);
+        if(auditableFacet == null || auditableFacet.isDisabled()) {
+            return;
+        }
+
+        final Bookmark target = aap.getBookmark();
+        final String propertyId = aap.getPropertyId();
+        final String memberId = aap.getMemberId();
+
+        final IsisTransaction.PreAndPostValues papv = auditEntry.getValue();
+        final String preValue = papv.getPreString();
+        final String postValue = papv.getPostString();
+
+
+        final String targetClass = CommandUtil.targetClassNameFor(adapter);
+
+        final Command command = commandContext.getCommand();
+        final UUID transactionId = command.getTransactionId();
+
+        auditingServiceIfAny
+                .audit(transactionId, targetClass, target, memberId, propertyId, preValue, postValue, user, timestamp);
+    }
+
+    /**
+     * could be null if none has been registered.
+     */
+    @Inject
+    private AuditingService3 auditingServiceIfAny;
+
+    @Inject
+    UserService userService;
+
+    @Inject
+    ClockService clockService;
+
+    @Inject
+    CommandContext commandContext;
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/5870f6a5/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
index 0f2747e..99b3079 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
@@ -41,9 +41,7 @@ import org.slf4j.LoggerFactory;
 
 import org.apache.isis.applib.annotation.Bulk;
 import org.apache.isis.applib.annotation.PublishedObject.ChangeKind;
-import org.apache.isis.applib.clock.Clock;
 import org.apache.isis.applib.services.actinvoc.ActionInvocationContext;
-import org.apache.isis.applib.services.audit.AuditingService3;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.command.Command2;
@@ -60,8 +58,6 @@ import org.apache.isis.core.commons.util.ToString;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
-import org.apache.isis.core.metamodel.facets.actions.action.invocation.CommandUtil;
-import org.apache.isis.core.metamodel.facets.object.audit.AuditableFacet;
 import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
 import org.apache.isis.core.metamodel.services.publishing.PublishingServiceInternal;
 import org.apache.isis.core.metamodel.spec.feature.Contributed;
@@ -70,6 +66,7 @@ import org.apache.isis.core.metamodel.transactions.TransactionState;
 import org.apache.isis.core.runtime.persistence.objectstore.transaction.CreateObjectCommand;
 import org.apache.isis.core.runtime.persistence.objectstore.transaction.DestroyObjectCommand;
 import org.apache.isis.core.runtime.persistence.objectstore.transaction.PersistenceCommand;
+import org.apache.isis.core.runtime.services.publishing.AuditingServiceInternal;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 
 import static org.apache.isis.core.commons.ensure.Ensure.ensureThatArg;
@@ -219,11 +216,8 @@ public class IsisTransaction implements TransactionScopedComponent {
 
     private final InteractionContext interactionContext;
     private final PublishingServiceInternal publishingServiceInternal;
+    private final AuditingServiceInternal auditingServiceInternal;
 
-    /**
-     * could be null if none has been registered.
-     */
-    private final AuditingService3 auditingServiceIfAny;
 
     private final UUID transactionId;
         
@@ -252,8 +246,8 @@ public class IsisTransaction implements TransactionScopedComponent {
 
         this.interactionContext = lookupService(InteractionContext.class);
 
-        this.auditingServiceIfAny = lookupServiceIfAny(AuditingService3.class);
         this.publishingServiceInternal = lookupService(PublishingServiceInternal.class);
+        this.auditingServiceInternal = lookupService(AuditingServiceInternal.class);
 
 
         this.transactionId = transactionId;
@@ -419,53 +413,6 @@ public class IsisTransaction implements TransactionScopedComponent {
         
     }
 
-    protected void doAudit(final Set<Entry<AdapterAndProperty, PreAndPostValues>> changedObjectProperties) {
-        try {
-            if(auditingServiceIfAny == null) {
-                return;
-            }
-
-            // else
-            final String currentUser = getTransactionManager().getAuthenticationSession().getUserName();
-            final java.sql.Timestamp currentTime = Clock.getTimeAsJavaSqlTimestamp();
-            for (Entry<AdapterAndProperty, PreAndPostValues> auditEntry : changedObjectProperties) {
-                auditChangedProperty(currentTime, currentUser, auditEntry);
-            }
-
-        } finally {
-            // not needed in production, but is required for integration testing
-            this.changedObjectProperties.clear();
-        }
-    }
-
-    public void auditChangedProperty(
-            final java.sql.Timestamp timestamp,
-            final String user,
-            final Entry<AdapterAndProperty, PreAndPostValues> auditEntry) {
-
-        final AdapterAndProperty aap = auditEntry.getKey();
-        final ObjectAdapter adapter = aap.getAdapter();
-        
-        final AuditableFacet auditableFacet = adapter.getSpecification().getFacet(AuditableFacet.class);
-        if(auditableFacet == null || auditableFacet.isDisabled()) {
-            return;
-        }
-
-        final Bookmark target = aap.getBookmark();
-        final String propertyId = aap.getPropertyId();
-        final String memberId = aap.getMemberId();
-
-        final PreAndPostValues papv = auditEntry.getValue();
-        final String preValue = papv.getPreString();
-        final String postValue = papv.getPostString();
-        
-
-        final String targetClass = CommandUtil.targetClassNameFor(adapter);
-
-        auditingServiceIfAny
-                .audit(getTransactionId(), targetClass, target, memberId, propertyId, preValue, postValue, user, timestamp);
-    }
-
     private static String asString(Object object) {
         return object != null? object.toString(): null;
     }
@@ -525,7 +472,21 @@ public class IsisTransaction implements TransactionScopedComponent {
 
             ensureCommandsPersistedIfDirtyXactn(changedObjectProperties);
 
-            preCommitServices(changedObjectProperties);
+            try {
+                auditingServiceInternal.audit(changedObjectProperties);
+            } finally {
+                // not needed in production, but is required for integration testing
+                this.changedObjectProperties.clear();
+            }
+
+            publishingServiceInternal.publishObjects(this.changeKindByEnlistedAdapter);
+            doFlush();
+
+            closeOtherApplibServicesIfConfigured();
+
+            completeCommandAndInteractionAndClearDomainEvents();
+            doFlush();
+
 
         } catch (final RuntimeException ex) {
             setAbortCause(new IsisTransactionManagerException(ex));
@@ -557,22 +518,6 @@ public class IsisTransaction implements TransactionScopedComponent {
                         Predicates.not(IS_COMMAND)));
     }
 
-
-    private void preCommitServices(
-            final Set<Entry<AdapterAndProperty, PreAndPostValues>> changedObjectProperties) {
-
-        doAudit(changedObjectProperties);
-
-        publishingServiceInternal.publishObjects(this.changeKindByEnlistedAdapter);
-        doFlush();
-
-        closeOtherApplibServicesIfConfigured();
-
-        completeCommandAndInteractionAndClearDomainEvents();
-
-        doFlush();
-    }
-
     private void closeOtherApplibServicesIfConfigured() {
         final ActionInvocationContext actionInvocationContext = lookupService(ActionInvocationContext.class);
         if(actionInvocationContext != null) {


[3/3] isis git commit: ISIS-1370: factored out EnlistedObjectsServiceInternal from IsisTransaction; used by AuditingServiceInternal and PublishingServiceInternal(Default).

Posted by da...@apache.org.
ISIS-1370: factored out EnlistedObjectsServiceInternal from IsisTransaction; used by AuditingServiceInternal and PublishingServiceInternal(Default).


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/ad43cad3
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/ad43cad3
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/ad43cad3

Branch: refs/heads/ISIS-1291
Commit: ad43cad3fc029d2b9f90232d7fc270bbbe30eb7e
Parents: e2fb62e
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Sun May 1 12:16:39 2016 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Sun May 1 12:16:39 2016 +0100

----------------------------------------------------------------------
 .../publishing/PublishingServiceInternal.java   |   7 +-
 .../auditing/AuditingServiceInternal.java       |  40 +-
 .../services/enlist/AdapterAndProperty.java     | 125 ++++++
 .../enlist/EnlistedObjectsServiceInternal.java  | 256 +++++++++++
 .../services/enlist/PreAndPostValues.java       | 104 +++++
 .../PublishingServiceInternalDefault.java       |  16 +-
 .../system/persistence/PersistenceSession.java  |  18 +-
 .../system/transaction/IsisTransaction.java     | 437 +------------------
 8 files changed, 557 insertions(+), 446 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/ad43cad3/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/publishing/PublishingServiceInternal.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/publishing/PublishingServiceInternal.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/publishing/PublishingServiceInternal.java
index 036731f..b0ed8c1 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/publishing/PublishingServiceInternal.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/publishing/PublishingServiceInternal.java
@@ -19,21 +19,19 @@
 package org.apache.isis.core.metamodel.services.publishing;
 
 import java.util.List;
-import java.util.Map;
 
 import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.annotation.PublishedObject;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 
 public interface PublishingServiceInternal {
+
     @Programmatic
     boolean canPublish();
 
     @Programmatic
-    void publishObjects(
-            final Map<ObjectAdapter, PublishedObject.ChangeKind> changeKindByEnlistedAdapter);
+    void publishObjects();
 
     @Programmatic
     void publishAction(
@@ -42,5 +40,4 @@ public interface PublishingServiceInternal {
             final ObjectAdapter targetAdapter,
             final List<ObjectAdapter> parameterAdapters,
             final ObjectAdapter resultAdapter);
-
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/ad43cad3/core/runtime/src/main/java/org/apache/isis/core/runtime/services/auditing/AuditingServiceInternal.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/auditing/AuditingServiceInternal.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/auditing/AuditingServiceInternal.java
index a470946..35874de 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/auditing/AuditingServiceInternal.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/auditing/AuditingServiceInternal.java
@@ -36,7 +36,9 @@ import org.apache.isis.applib.services.user.UserService;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.facets.actions.action.invocation.CommandUtil;
 import org.apache.isis.core.metamodel.facets.object.audit.AuditableFacet;
-import org.apache.isis.core.runtime.system.transaction.IsisTransaction;
+import org.apache.isis.core.runtime.services.enlist.AdapterAndProperty;
+import org.apache.isis.core.runtime.services.enlist.EnlistedObjectsServiceInternal;
+import org.apache.isis.core.runtime.services.enlist.PreAndPostValues;
 
 /**
  * Wrapper around {@link org.apache.isis.applib.services.audit.AuditingService3}.  Is a no-op if there is no injected service.
@@ -51,28 +53,34 @@ public class AuditingServiceInternal {
     }
 
     @Programmatic
-    public void audit(
-            final Set<Map.Entry<IsisTransaction.AdapterAndProperty, IsisTransaction.PreAndPostValues>> changedObjectProperties) {
+    public void audit() {
+        final Set<Map.Entry<AdapterAndProperty, PreAndPostValues>> changedObjectProperties =
+                enlistedObjectsServiceInternal.getChangedObjectProperties();
 
-        if(!canAudit()) {
-            return;
-        }
+        try {
+            if(!canAudit()) {
+                return;
+            }
 
-        final String currentUser = userService.getUser().getName();
-        final java.sql.Timestamp currentTime = clockService.nowAsJavaSqlTimestamp();
+            final String currentUser = userService.getUser().getName();
+            final java.sql.Timestamp currentTime = clockService.nowAsJavaSqlTimestamp();
 
-        for (Map.Entry<IsisTransaction.AdapterAndProperty, IsisTransaction.PreAndPostValues> auditEntry : changedObjectProperties) {
-            auditChangedProperty(currentTime, currentUser, auditEntry);
-        }
+            for (Map.Entry<AdapterAndProperty, PreAndPostValues> auditEntry : changedObjectProperties) {
+                auditChangedProperty(currentTime, currentUser, auditEntry);
+            }
 
+        } finally {
+            // not needed in production, but is required for integration testing
+            enlistedObjectsServiceInternal.clearChangedObjectProperties();
+        }
     }
 
     private void auditChangedProperty(
             final java.sql.Timestamp timestamp,
             final String user,
-            final Map.Entry<IsisTransaction.AdapterAndProperty, IsisTransaction.PreAndPostValues> auditEntry) {
+            final Map.Entry<AdapterAndProperty, PreAndPostValues> auditEntry) {
 
-        final IsisTransaction.AdapterAndProperty aap = auditEntry.getKey();
+        final AdapterAndProperty aap = auditEntry.getKey();
         final ObjectAdapter adapter = aap.getAdapter();
 
         final AuditableFacet auditableFacet = adapter.getSpecification().getFacet(AuditableFacet.class);
@@ -84,7 +92,7 @@ public class AuditingServiceInternal {
         final String propertyId = aap.getPropertyId();
         final String memberId = aap.getMemberId();
 
-        final IsisTransaction.PreAndPostValues papv = auditEntry.getValue();
+        final PreAndPostValues papv = auditEntry.getValue();
         final String preValue = papv.getPreString();
         final String postValue = papv.getPostString();
 
@@ -105,6 +113,9 @@ public class AuditingServiceInternal {
     private AuditingService3 auditingServiceIfAny;
 
     @Inject
+    private EnlistedObjectsServiceInternal enlistedObjectsServiceInternal;
+
+    @Inject
     UserService userService;
 
     @Inject
@@ -113,5 +124,4 @@ public class AuditingServiceInternal {
     @Inject
     CommandContext commandContext;
 
-
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/ad43cad3/core/runtime/src/main/java/org/apache/isis/core/runtime/services/enlist/AdapterAndProperty.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/enlist/AdapterAndProperty.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/enlist/AdapterAndProperty.java
new file mode 100644
index 0000000..ef39483
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/enlist/AdapterAndProperty.java
@@ -0,0 +1,125 @@
+/*
+ *  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.runtime.services.enlist;
+
+import java.util.Map;
+
+import com.google.common.base.Function;
+
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.oid.RootOid;
+import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+
+public class AdapterAndProperty {
+
+    private final ObjectAdapter objectAdapter;
+    private final ObjectAssociation property;
+    private final Bookmark bookmark;
+    private final String propertyId;
+    private final String bookmarkStr;
+
+    public static AdapterAndProperty of(ObjectAdapter adapter, ObjectAssociation property) {
+        return new AdapterAndProperty(adapter, property);
+    }
+
+    private AdapterAndProperty(ObjectAdapter adapter, ObjectAssociation property) {
+        this.objectAdapter = adapter;
+        this.property = property;
+
+        final RootOid oid = (RootOid) adapter.getOid();
+
+        final String objectType = oid.getObjectSpecId().asString();
+        final String identifier = oid.getIdentifier();
+        bookmark = new Bookmark(objectType, identifier);
+        bookmarkStr = bookmark.toString();
+
+        propertyId = property.getId();
+    }
+
+    public ObjectAdapter getAdapter() {
+        return objectAdapter;
+    }
+
+    public ObjectAssociation getProperty() {
+        return property;
+    }
+
+    public Bookmark getBookmark() {
+        return bookmark;
+    }
+
+    public String getPropertyId() {
+        return propertyId;
+    }
+
+    public String getMemberId() {
+        return property.getIdentifier().toClassAndNameIdentityString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        final AdapterAndProperty that = (AdapterAndProperty) o;
+
+        if (bookmarkStr != null ? !bookmarkStr.equals(that.bookmarkStr) : that.bookmarkStr != null)
+            return false;
+        if (propertyId != null ? !propertyId.equals(that.propertyId) : that.propertyId != null)
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = propertyId != null ? propertyId.hashCode() : 0;
+        result = 31 * result + (bookmarkStr != null ? bookmarkStr.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return bookmarkStr + " , " + getProperty().getId();
+    }
+
+    public Object getPropertyValue() {
+        ObjectAdapter referencedAdapter = property.get(objectAdapter, InteractionInitiatedBy.FRAMEWORK);
+        return referencedAdapter == null ? null : referencedAdapter.getObject();
+    }
+
+    static class Functions {
+        private Functions() {
+        }
+
+        static final Function<Map.Entry<AdapterAndProperty, PreAndPostValues>, ObjectAdapter> GET_ADAPTER = new Function<Map.Entry<AdapterAndProperty, PreAndPostValues>, ObjectAdapter>() {
+            @Override
+            public ObjectAdapter apply(Map.Entry<AdapterAndProperty, PreAndPostValues> input) {
+                final AdapterAndProperty aap = input.getKey();
+                return aap.getAdapter();
+            }
+        };
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/ad43cad3/core/runtime/src/main/java/org/apache/isis/core/runtime/services/enlist/EnlistedObjectsServiceInternal.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/enlist/EnlistedObjectsServiceInternal.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/enlist/EnlistedObjectsServiceInternal.java
new file mode 100644
index 0000000..1839b6e
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/enlist/EnlistedObjectsServiceInternal.java
@@ -0,0 +1,256 @@
+/*
+ *  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.runtime.services.enlist;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import javax.enterprise.context.RequestScoped;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.annotation.PublishedObject;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.Contributed;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.runtime.system.transaction.IsisTransaction;
+
+@DomainService(nature = NatureOfService.DOMAIN)
+@RequestScoped
+public class EnlistedObjectsServiceInternal {
+
+    // used for auditing
+    private final Map<AdapterAndProperty, PreAndPostValues> changedObjectProperties = Maps.newLinkedHashMap();
+
+    // used for publishing
+    private final Map<ObjectAdapter,PublishedObject.ChangeKind> changeKindByEnlistedAdapter = Maps.newLinkedHashMap();
+
+    /**
+     * Auditing and publishing support: for object stores to enlist an object that has just been created,
+     * capturing a dummy value <tt>'[NEW]'</tt> for the pre-modification value.
+     *
+     * <p>
+     * The post-modification values are captured when the transaction commits.
+     *
+     * <p>
+     * Supported by the JDO object store; check documentation for support in other objectstores.
+     */
+    @Programmatic
+    public void enlistCreated(final ObjectAdapter adapter) {
+
+        enlistForPublishing(adapter, PublishedObject.ChangeKind.CREATE);
+
+        for (ObjectAssociation property : adapter.getSpecification().getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.PROPERTIES)) {
+            final AdapterAndProperty aap = AdapterAndProperty.of(adapter, property);
+            if(property.isNotPersisted()) {
+                continue;
+            }
+            if(changedObjectProperties.containsKey(aap)) {
+                // already enlisted, so ignore
+                return;
+            }
+            PreAndPostValues papv = PreAndPostValues.pre(IsisTransaction.Placeholder.NEW);
+            changedObjectProperties.put(aap, papv);
+        }
+    }
+
+    /**
+     * Auditing and publishing support: for object stores to enlist an object that is about to be updated,
+     * capturing the pre-modification values of the properties of the {@link ObjectAdapter}.
+     *
+     * <p>
+     * The post-modification values are captured when the transaction commits.
+     *
+     * <p>
+     * Supported by the JDO object store; check documentation for support in other objectstores.
+     */
+    @Programmatic
+    public void enlistUpdating(final ObjectAdapter adapter) {
+
+        enlistForPublishing(adapter, PublishedObject.ChangeKind.UPDATE);
+
+        for (ObjectAssociation property : adapter.getSpecification().getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.PROPERTIES)) {
+            final AdapterAndProperty aap = AdapterAndProperty.of(adapter, property);
+            if(property.isNotPersisted()) {
+                continue;
+            }
+            if(changedObjectProperties.containsKey(aap)) {
+                // already enlisted, so ignore
+                return;
+            }
+            PreAndPostValues papv = PreAndPostValues.pre(aap.getPropertyValue());
+            changedObjectProperties.put(aap, papv);
+        }
+    }
+
+    /**
+     * Auditing and publishing support: for object stores to enlist an object that is about to be deleted,
+     * capturing the pre-deletion value of the properties of the {@link ObjectAdapter}.
+     *
+     * <p>
+     * The post-modification values are captured  when the transaction commits.  In the case of deleted objects, a
+     * dummy value <tt>'[DELETED]'</tt> is used as the post-modification value.
+     *
+     * <p>
+     * Supported by the JDO object store; check documentation for support in other objectstores.
+     */
+    @Programmatic
+    public void enlistDeleting(final ObjectAdapter adapter) {
+
+        final boolean enlisted = enlistForPublishing(adapter, PublishedObject.ChangeKind.DELETE);
+        if(!enlisted) {
+            return;
+        }
+
+        for (ObjectAssociation property : adapter.getSpecification().getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.PROPERTIES)) {
+            final AdapterAndProperty aap = AdapterAndProperty.of(adapter, property);
+            if(property.isNotPersisted()) {
+                continue;
+            }
+            if(changedObjectProperties.containsKey(aap)) {
+                // already enlisted, so ignore
+                return;
+            }
+            PreAndPostValues papv = PreAndPostValues.pre(aap.getPropertyValue());
+            changedObjectProperties.put(aap, papv);
+        }
+    }
+
+
+    /**
+     *
+     * @param adapter
+     * @param current
+     * @return <code>true</code> if successfully enlisted, <code>false</code> if was already enlisted
+     */
+    private boolean enlistForPublishing(final ObjectAdapter adapter, final PublishedObject.ChangeKind current) {
+        final PublishedObject.ChangeKind previous = changeKindByEnlistedAdapter.get(adapter);
+        if(previous == null) {
+            changeKindByEnlistedAdapter.put(adapter, current);
+            return true;
+        }
+        switch (previous) {
+        case CREATE:
+            switch (current) {
+            case DELETE:
+                changeKindByEnlistedAdapter.remove(adapter);
+            case CREATE:
+            case UPDATE:
+                return false;
+            }
+            break;
+        case UPDATE:
+            switch (current) {
+            case DELETE:
+                changeKindByEnlistedAdapter.put(adapter, current);
+                return true;
+            case CREATE:
+            case UPDATE:
+                return false;
+            }
+            break;
+        case DELETE:
+            return false;
+        }
+        return previous == null;
+    }
+
+
+    @Programmatic
+    public boolean hasChangedAdapters() {
+        final Set<Map.Entry<AdapterAndProperty, PreAndPostValues>> changedObjectProperties = getChangedObjectProperties();
+
+        final Set<ObjectAdapter> changedAdapters = Sets.newHashSet(
+                Iterables.filter(
+                        Iterables.transform(
+                                changedObjectProperties,
+                                AdapterAndProperty.Functions.GET_ADAPTER),
+                        Predicates.not(IS_COMMAND)));
+        return !changedAdapters.isEmpty();
+    }
+
+    private static final Predicate<ObjectAdapter> IS_COMMAND = new Predicate<ObjectAdapter>() {
+        @Override
+        public boolean apply(ObjectAdapter input) {
+            return Command.class.isAssignableFrom(input.getSpecification().getCorrespondingClass());
+        }
+    };
+
+    @Programmatic
+    public Set<Map.Entry<AdapterAndProperty, PreAndPostValues>> getChangedObjectProperties() {
+        final Map<AdapterAndProperty, PreAndPostValues> processedObjectProperties =  getAdapterAndPropertyPreAndPostValuesMap();
+
+        return Collections.unmodifiableSet(
+                Sets.filter(processedObjectProperties.entrySet(), PreAndPostValues.Predicates.CHANGED));
+    }
+
+    private Map<AdapterAndProperty, PreAndPostValues> getAdapterAndPropertyPreAndPostValuesMap() {
+        final Map<AdapterAndProperty, PreAndPostValues> processedObjectProperties = Maps.newLinkedHashMap();
+
+        while(!changedObjectProperties.isEmpty()) {
+
+            final Set<AdapterAndProperty> keys = Sets.newLinkedHashSet(changedObjectProperties.keySet());
+            for (final AdapterAndProperty aap : keys) {
+
+                final PreAndPostValues papv = changedObjectProperties.remove(aap);
+
+                final ObjectAdapter adapter = aap.getAdapter();
+                if(adapter.isDestroyed()) {
+                    // don't touch the object!!!
+                    // JDO, for example, will complain otherwise...
+                    papv.setPost(IsisTransaction.Placeholder.DELETED);
+                } else {
+                    papv.setPost(aap.getPropertyValue());
+                }
+
+                // if we encounter the same objectProperty again, this will simply overwrite it
+                processedObjectProperties.put(aap, papv);
+            }
+        }
+        return processedObjectProperties;
+    }
+
+    @Programmatic
+    public void clearChangedObjectProperties() {
+        changedObjectProperties.clear();
+    }
+
+
+    @Programmatic
+    public Map<ObjectAdapter, PublishedObject.ChangeKind> getChangeKindByEnlistedAdapter() {
+        return changeKindByEnlistedAdapter;
+    }
+
+
+    static String asString(Object object) {
+        return object != null? object.toString(): null;
+    }
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/ad43cad3/core/runtime/src/main/java/org/apache/isis/core/runtime/services/enlist/PreAndPostValues.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/enlist/PreAndPostValues.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/enlist/PreAndPostValues.java
new file mode 100644
index 0000000..c9d59be
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/enlist/PreAndPostValues.java
@@ -0,0 +1,104 @@
+/*
+ *  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.runtime.services.enlist;
+
+import java.util.Map;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+
+import org.apache.isis.core.runtime.system.transaction.IsisTransaction;
+
+public class PreAndPostValues {
+
+    public static class Predicates {
+        public final static Predicate<Map.Entry<?, PreAndPostValues>> CHANGED = new Predicate<Map.Entry<?, PreAndPostValues>>() {
+            @Override
+            public boolean apply(Map.Entry<?, PreAndPostValues> input) {
+                final PreAndPostValues papv = input.getValue();
+                return papv.differ();
+            }
+        };
+    }
+
+    private final Object pre;
+    /**
+     * Eagerly calculated because it could be that the object referenced ends up being deleted by the time that the xactn completes.
+     */
+    private final String preString;
+
+    /**
+     * Updated in {@link #setPost(Object)}
+     */
+    private Object post;
+    /**
+     * Updated in {@link #setPost(Object)}, along with {@link #post}.
+     */
+    private String postString;
+
+    public static PreAndPostValues pre(Object preValue) {
+        return new PreAndPostValues(preValue, null);
+    }
+
+    private PreAndPostValues(Object pre, Object post) {
+        this.pre = pre;
+        this.post = post;
+        this.preString = EnlistedObjectsServiceInternal.asString(pre);
+    }
+
+    /**
+     * The object that was referenced before this object was changed
+     * <p/>
+     * <p/>
+     * Note that this referenced object itself could end up being deleted in the course of the transaction; in which case use
+     * {@link #getPreString()} which is the eagerly cached <tt>toString</tt> of said object.
+     */
+    public Object getPre() {
+        return pre;
+    }
+
+    public String getPreString() {
+        return preString;
+    }
+
+    public Object getPost() {
+        return post;
+    }
+
+    public String getPostString() {
+        return postString;
+    }
+
+    public void setPost(Object post) {
+        this.post = post;
+        this.postString = EnlistedObjectsServiceInternal.asString(post);
+    }
+
+    @Override
+    public String toString() {
+        return getPre() + " -> " + getPost();
+    }
+
+    public boolean differ() {
+        if (getPre() == IsisTransaction.Placeholder.NEW || getPost() == IsisTransaction.Placeholder.DELETED) {
+            return true;
+        }
+        return !Objects.equal(getPre(), getPost());
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/ad43cad3/core/runtime/src/main/java/org/apache/isis/core/runtime/services/publishing/PublishingServiceInternalDefault.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/publishing/PublishingServiceInternalDefault.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/publishing/PublishingServiceInternalDefault.java
index b860d2b..8d5441d 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/publishing/PublishingServiceInternalDefault.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/publishing/PublishingServiceInternalDefault.java
@@ -62,6 +62,7 @@ import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
 import org.apache.isis.core.metamodel.facets.object.publishedobject.PublishedObjectFacet;
 import org.apache.isis.core.metamodel.services.publishing.PublishingServiceInternal;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.runtime.services.enlist.EnlistedObjectsServiceInternal;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 import org.apache.isis.core.runtime.system.persistence.PersistenceSession;
 import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager;
@@ -107,18 +108,23 @@ public class PublishingServiceInternalDefault implements PublishingServiceIntern
         throw new IllegalArgumentException("unknown ChangeKind '" + changeKind + "'");
     }
 
-    @Override @Programmatic
+    @Override
+    @Programmatic
     public boolean canPublish() {
         return publishingServiceIfAny != null;
     }
 
-    @Override @Programmatic
-    public void publishObjects(final Map<ObjectAdapter, ChangeKind> changeKindByEnlistedAdapter) {
+    @Override
+    @Programmatic
+    public void publishObjects() {
 
         if(!canPublish()) {
             return;
         }
 
+        final Map<ObjectAdapter, ChangeKind> changeKindByEnlistedAdapter =
+                enlistedObjectsServiceInternal.getChangeKindByEnlistedAdapter();
+
         final String currentUser = userService.getUser().getName();
         final Timestamp timestamp = clockService.nowAsJavaSqlTimestamp();
         final ObjectStringifier stringifier = objectStringifier();
@@ -234,6 +240,7 @@ public class PublishingServiceInternalDefault implements PublishingServiceIntern
         publishingServiceIfAny.publish(metadata, payload);
     }
 
+
     private static <T> List<T> immutableList(final Iterable<T> iterable) {
         return Collections.unmodifiableList(Lists.newArrayList(iterable));
     }
@@ -301,6 +308,9 @@ public class PublishingServiceInternalDefault implements PublishingServiceIntern
     private PublishingService publishingServiceIfAny;
 
     @Inject
+    private EnlistedObjectsServiceInternal enlistedObjectsServiceInternal;
+
+    @Inject
     private CommandContext commandContext;
 
     @Inject

http://git-wip-us.apache.org/repos/asf/isis/blob/ad43cad3/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java
index 88f4ba2..b05e4b9 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java
@@ -122,6 +122,7 @@ import org.apache.isis.core.runtime.persistence.objectstore.transaction.Transact
 import org.apache.isis.core.runtime.persistence.query.PersistenceQueryFindAllInstances;
 import org.apache.isis.core.runtime.persistence.query.PersistenceQueryFindUsingApplibQueryDefault;
 import org.apache.isis.core.runtime.runner.opts.OptionHandlerFixtureAbstract;
+import org.apache.isis.core.runtime.services.enlist.EnlistedObjectsServiceInternal;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 import org.apache.isis.core.runtime.system.persistence.adaptermanager.OidAdapterHashMap;
 import org.apache.isis.core.runtime.system.persistence.adaptermanager.PojoAdapterHashMap;
@@ -2045,8 +2046,10 @@ public class PersistenceSession implements
     public void enlistDeletingAndInvokeIsisRemovingCallbackFacet(final Persistable pojo) {
         ObjectAdapter adapter = adapterFor(pojo);
 
-        final IsisTransaction transaction = getCurrentTransaction();
-        transaction.enlistDeleting(adapter);
+        final EnlistedObjectsServiceInternal enlistedObjectsServiceInternal =
+                getServicesInjector().lookupService(EnlistedObjectsServiceInternal.class);
+
+        enlistedObjectsServiceInternal.enlistDeleting(adapter);
 
         CallbackFacet.Util.callCallback(adapter, RemovingCallbackFacet.class);
         postLifecycleEventIfRequired(adapter, RemovingLifecycleEventFacet.class);
@@ -2237,8 +2240,10 @@ public class PersistenceSession implements
             postLifecycleEventIfRequired(adapter, PersistedLifecycleEventFacet.class);
 
 
-            final IsisTransaction transaction = getCurrentTransaction();
-            transaction.enlistCreated(adapter);
+            final EnlistedObjectsServiceInternal enlistedObjectsServiceInternal =
+                    getServicesInjector().lookupService(EnlistedObjectsServiceInternal.class);
+
+            enlistedObjectsServiceInternal.enlistCreated(adapter);
 
         } else {
             // updating;
@@ -2283,7 +2288,10 @@ public class PersistenceSession implements
         postLifecycleEventIfRequired(adapter, UpdatingLifecycleEventFacet.class);
 
 
-        getCurrentTransaction().enlistUpdating(adapter);
+        final EnlistedObjectsServiceInternal enlistedObjectsServiceInternal =
+                getServicesInjector().lookupService(EnlistedObjectsServiceInternal.class);
+
+        enlistedObjectsServiceInternal.enlistUpdating(adapter);
 
         ensureRootObject(pojo);
     }

http://git-wip-us.apache.org/repos/asf/isis/blob/ad43cad3/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
index e49601c..54abc1e 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
@@ -20,29 +20,16 @@
 package org.apache.isis.core.runtime.system.transaction;
 
 import java.sql.Timestamp;
-import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
 import java.util.UUID;
 
-import com.google.common.base.Function;
-import com.google.common.base.Objects;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.isis.applib.annotation.Bulk;
-import org.apache.isis.applib.annotation.PublishedObject.ChangeKind;
 import org.apache.isis.applib.services.actinvoc.ActionInvocationContext;
-import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.command.Command2;
 import org.apache.isis.applib.services.command.Command3;
@@ -56,18 +43,14 @@ import org.apache.isis.core.commons.components.TransactionScopedComponent;
 import org.apache.isis.core.commons.exceptions.IsisException;
 import org.apache.isis.core.commons.util.ToString;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.core.metamodel.adapter.oid.RootOid;
-import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
 import org.apache.isis.core.metamodel.services.publishing.PublishingServiceInternal;
-import org.apache.isis.core.metamodel.spec.feature.Contributed;
-import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.metamodel.transactions.TransactionState;
 import org.apache.isis.core.runtime.persistence.objectstore.transaction.CreateObjectCommand;
 import org.apache.isis.core.runtime.persistence.objectstore.transaction.DestroyObjectCommand;
 import org.apache.isis.core.runtime.persistence.objectstore.transaction.PersistenceCommand;
 import org.apache.isis.core.runtime.services.auditing.AuditingServiceInternal;
-import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.runtime.services.enlist.EnlistedObjectsServiceInternal;
 
 import static org.apache.isis.core.commons.ensure.Ensure.ensureThatArg;
 import static org.apache.isis.core.commons.ensure.Ensure.ensureThatState;
@@ -89,17 +72,9 @@ import static org.hamcrest.Matchers.not;
  */
 public class IsisTransaction implements TransactionScopedComponent {
 
-
-    public static final Predicate<ObjectAdapter> IS_COMMAND = new Predicate<ObjectAdapter>() {
-        @Override
-        public boolean apply(ObjectAdapter input) {
-            return Command.class.isAssignableFrom(input.getSpecification().getCorrespondingClass());
-        }
-    };
-
-    private static class Placeholder {
-        private static Placeholder NEW = new Placeholder("[NEW]");
-        private static Placeholder DELETED = new Placeholder("[DELETED]");
+    public static class Placeholder {
+        public static Placeholder NEW = new Placeholder("[NEW]");
+        public static Placeholder DELETED = new Placeholder("[DELETED]");
         private final String str;
         public Placeholder(String str) {
             this.str = str;
@@ -156,7 +131,7 @@ public class IsisTransaction implements TransactionScopedComponent {
 
         public final TransactionState transactionState;
         
-        private State(TransactionState transactionState){
+        State(TransactionState transactionState){
             this.transactionState = transactionState;
         }
 
@@ -217,6 +192,7 @@ public class IsisTransaction implements TransactionScopedComponent {
     private final InteractionContext interactionContext;
     private final PublishingServiceInternal publishingServiceInternal;
     private final AuditingServiceInternal auditingServiceInternal;
+    private final EnlistedObjectsServiceInternal enlistedObjectsServiceInternal;
 
 
     private final UUID transactionId;
@@ -248,6 +224,7 @@ public class IsisTransaction implements TransactionScopedComponent {
 
         this.publishingServiceInternal = lookupService(PublishingServiceInternal.class);
         this.auditingServiceInternal = lookupService(AuditingServiceInternal.class);
+        this.enlistedObjectsServiceInternal = lookupService(EnlistedObjectsServiceInternal.class);
 
 
         this.transactionId = transactionId;
@@ -349,13 +326,6 @@ public class IsisTransaction implements TransactionScopedComponent {
     }
 
     /**
-     * Maximum number of times we attempt to flush the transaction before giving up.
-     */
-    private final static int MAX_FLUSH_ATTEMPTS = 10;
-    
-    /**
-     * Mandatory hook method for subclasses to persist all pending changes.
-     * 
      * <p>
      * Called by both {@link #commit()} and by {@link #flush()}:
      * <table>
@@ -378,7 +348,6 @@ public class IsisTransaction implements TransactionScopedComponent {
      */
     private void doFlush() {
         
-        int i = 0;
         //
         // it's possible that in executing these commands that more will be created.
         // so we keep flushing until no more are available (ISIS-533)
@@ -413,17 +382,7 @@ public class IsisTransaction implements TransactionScopedComponent {
         
     }
 
-    private static String asString(Object object) {
-        return object != null? object.toString(): null;
-    }
-
-
-    protected AuthenticationSession getAuthenticationSession() {
-        return IsisContext.getAuthenticationSession();
-    }
-
 
-    
     // ////////////////////////////////////////////////////////////////
     // preCommit, commit
     // ////////////////////////////////////////////////////////////////
@@ -444,45 +403,24 @@ public class IsisTransaction implements TransactionScopedComponent {
         }
 
         try {
-            final Map<AdapterAndProperty, PreAndPostValues> processedObjectProperties = Maps.newLinkedHashMap();
-            while(!changedObjectProperties.isEmpty()) {
+            // ensureCommandsPersistedIfDirtyXactn
+            final Command command = commandContext.getCommand();
 
-                final Set<AdapterAndProperty> keys = Sets.newLinkedHashSet(changedObjectProperties.keySet());
-                for (final AdapterAndProperty aap : keys) {
-
-                    final PreAndPostValues papv = changedObjectProperties.remove(aap);
-
-                    final ObjectAdapter adapter = aap.getAdapter();
-                    if(adapter.isDestroyed()) {
-                        // don't touch the object!!!
-                        // JDO, for example, will complain otherwise...
-                        papv.setPost(Placeholder.DELETED);
-                    } else {
-                        papv.setPost(aap.getPropertyValue());
-                    }
-
-                    // if we encounter the same objectProperty again, this will simply overwrite it
-                    processedObjectProperties.put(aap, papv);
-                }
+            // ensure that any changed objects means that the command should be persisted
+            final boolean hasChangedAdapters = enlistedObjectsServiceInternal.hasChangedAdapters();
+            if(hasChangedAdapters && command.getMemberIdentifier() != null) {
+                command.setPersistHint(true);
             }
 
-            final Set<Entry<AdapterAndProperty, PreAndPostValues>> changedObjectProperties =
-                    Collections.unmodifiableSet(
-                            Sets.filter(processedObjectProperties.entrySet(), PreAndPostValues.Predicates.CHANGED));
-
-            ensureCommandsPersistedIfDirtyXactn(changedObjectProperties);
-
-            try {
-                auditingServiceInternal.audit(changedObjectProperties);
-            } finally {
-                // not needed in production, but is required for integration testing
-                this.changedObjectProperties.clear();
-            }
+            auditingServiceInternal.audit();
 
-            publishingServiceInternal.publishObjects(this.changeKindByEnlistedAdapter);
+            publishingServiceInternal.publishObjects();
             doFlush();
 
-            closeOtherApplibServicesIfConfigured();
+            final ActionInvocationContext actionInvocationContext = lookupService(ActionInvocationContext.class);
+            if(actionInvocationContext != null) {
+                Bulk.InteractionContext.current.set(null);
+            }
 
             completeCommandAndInteractionAndClearDomainEvents();
             doFlush();
@@ -495,36 +433,6 @@ public class IsisTransaction implements TransactionScopedComponent {
         }
     }
 
-    private void ensureCommandsPersistedIfDirtyXactn(
-            final Set<Entry<AdapterAndProperty, PreAndPostValues>> changedObjectProperties) {
-
-        final Command command = commandContext.getCommand();
-
-        // ensure that any changed objects means that the command should be persisted
-        final Set<ObjectAdapter> changedAdapters = findChangedAdapters(changedObjectProperties);
-        if(!changedAdapters.isEmpty() && command.getMemberIdentifier() != null) {
-            command.setPersistHint(true);
-        }
-    }
-
-
-    private static Set<ObjectAdapter> findChangedAdapters(
-            final Set<Entry<AdapterAndProperty, PreAndPostValues>> changedObjectProperties) {
-        return Sets.newHashSet(
-                Iterables.filter(
-                        Iterables.transform(
-                                changedObjectProperties,
-                                AdapterAndProperty.Functions.GET_ADAPTER),
-                        Predicates.not(IS_COMMAND)));
-    }
-
-    private void closeOtherApplibServicesIfConfigured() {
-        final ActionInvocationContext actionInvocationContext = lookupService(ActionInvocationContext.class);
-        if(actionInvocationContext != null) {
-            Bulk.InteractionContext.current.set(null);
-        }
-    }
-
     private void completeCommandAndInteractionAndClearDomainEvents() {
 
         final Command command = commandContext.getCommand();
@@ -679,15 +587,6 @@ public class IsisTransaction implements TransactionScopedComponent {
     // Depenendencies (from constructor)
     // ////////////////////////////////////////////////////////////////
 
-    /**
-     * The owning {@link IsisTransactionManager transaction manager}.
-     * 
-     * <p>
-     * Injected in constructor
-     */
-    public IsisTransactionManager getTransactionManager() {
-        return transactionManager;
-    }
 
     /**
      * The {@link org.apache.isis.core.commons.authentication.MessageBroker} for this transaction.
@@ -701,302 +600,6 @@ public class IsisTransaction implements TransactionScopedComponent {
         return messageBroker;
     }
 
-    public static class AdapterAndProperty {
-        
-        private final ObjectAdapter objectAdapter;
-        private final ObjectAssociation property;
-        private final Bookmark bookmark;
-        private final String propertyId;
-        private final String bookmarkStr;
-
-        public static AdapterAndProperty of(ObjectAdapter adapter, ObjectAssociation property) {
-            return new AdapterAndProperty(adapter, property);
-        }
-
-        private AdapterAndProperty(ObjectAdapter adapter, ObjectAssociation property) {
-            this.objectAdapter = adapter;
-            this.property = property;
-
-            final RootOid oid = (RootOid) adapter.getOid();
-
-            final String objectType = oid.getObjectSpecId().asString();
-            final String identifier = oid.getIdentifier();
-            bookmark = new Bookmark(objectType, identifier);
-            bookmarkStr = bookmark.toString();
-
-            propertyId = property.getId();
-        }
-        
-        public ObjectAdapter getAdapter() {
-            return objectAdapter;
-        }
-        public ObjectAssociation getProperty() {
-            return property;
-        }
-
-        public Bookmark getBookmark() {
-            return bookmark;
-        }
-        public String getPropertyId() {
-            return propertyId;
-        }
-
-        public String getMemberId() {
-            return property.getIdentifier().toClassAndNameIdentityString();
-        }
-
-        @Override
-        public boolean equals(final Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-
-            final AdapterAndProperty that = (AdapterAndProperty) o;
-
-            if (bookmarkStr != null ? !bookmarkStr.equals(that.bookmarkStr) : that.bookmarkStr != null) return false;
-            if (propertyId != null ? !propertyId.equals(that.propertyId) : that.propertyId != null) return false;
-
-            return true;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = propertyId != null ? propertyId.hashCode() : 0;
-            result = 31 * result + (bookmarkStr != null ? bookmarkStr.hashCode() : 0);
-            return result;
-        }
-
-        @Override
-        public String toString() {
-            return bookmarkStr + " , " + getProperty().getId();
-        }
-
-
-        private Object getPropertyValue() {
-            ObjectAdapter referencedAdapter = property.get(objectAdapter, InteractionInitiatedBy.FRAMEWORK);
-            return referencedAdapter == null ? null : referencedAdapter.getObject();
-        }
-
-        static class Functions {
-            private Functions(){}
-
-            static final Function<Entry<AdapterAndProperty, PreAndPostValues>, ObjectAdapter> GET_ADAPTER = new Function<Entry<AdapterAndProperty, PreAndPostValues>, ObjectAdapter>() {
-                @Override
-                public ObjectAdapter apply(Entry<AdapterAndProperty, PreAndPostValues> input) {
-                    final AdapterAndProperty aap = input.getKey();
-                    return aap.getAdapter();
-                }
-            };
-
-        }
-
-    }
-
-    
-    ////////////////////////////////////////////////////////////////////////
-    // Auditing/Publishing object tracking
-    ////////////////////////////////////////////////////////////////////////
-
-    public static class PreAndPostValues {
-
-        static class Predicates {
-            final static Predicate<Entry<?, PreAndPostValues>> CHANGED = new Predicate<Entry<?, PreAndPostValues>>(){
-                @Override
-                public boolean apply(Entry<?, PreAndPostValues> input) {
-                    final PreAndPostValues papv = input.getValue();
-                    return papv.differ();
-                }};
-        }
-
-        private final Object pre;
-        /**
-         * Eagerly calculated because it could be that the object referenced ends up being deleted by the time that the xactn completes.
-         */
-        private final String preString;
-
-        /**
-         * Updated in {@link #setPost(Object)} 
-         */
-        private Object post;
-        /**
-         * Updated in {@link #setPost(Object)}, along with {@link #post}.
-         */
-        private String postString;
-
-        
-        public static PreAndPostValues pre(Object preValue) {
-            return new PreAndPostValues(preValue, null);
-        }
-
-        private PreAndPostValues(Object pre, Object post) {
-            this.pre = pre;
-            this.post = post;
-            this.preString = asString(pre);
-        }
-        /**
-         * The object that was referenced before this object was changed
-         * 
-         * <p>
-         * Note that this referenced object itself could end up being deleted in the course of the transaction; in which case use 
-         * {@link #getPreString()} which is the eagerly cached <tt>toString</tt> of said object. 
-         */
-        public Object getPre() {
-            return pre;
-        }
-        public String getPreString() {
-            return preString;
-        }
-        public Object getPost() {
-            return post;
-        }
-        public String getPostString() {
-            return postString;
-        }
-        public void setPost(Object post) {
-            this.post = post;
-            this.postString = asString(post);
-        }
-        
-        @Override
-        public String toString() {
-            return getPre() + " -> " + getPost();
-        }
-
-        public boolean differ() {
-            if(getPre() == Placeholder.NEW || getPost() == Placeholder.DELETED) {
-                return true;
-            }
-            return !Objects.equal(getPre(), getPost());
-        }
-    }
-    
-   
-    private final Map<ObjectAdapter,ChangeKind> changeKindByEnlistedAdapter = Maps.newLinkedHashMap();
-    private final Map<AdapterAndProperty, PreAndPostValues> changedObjectProperties = Maps.newLinkedHashMap();
-
-
-
-
-    /**
-     * Auditing and publishing support: for object stores to enlist an object that has just been created, 
-     * capturing a dummy value <tt>'[NEW]'</tt> for the pre-modification value. 
-     * 
-     * <p>
-     * The post-modification values are captured in {@link #preCommit()}.
-     * 
-     * <p>
-     * Supported by the JDO object store; check documentation for support in other objectstores.
-     */
-    public void enlistCreated(ObjectAdapter adapter) {
-        enlist(adapter, ChangeKind.CREATE);
-        for (ObjectAssociation property : adapter.getSpecification().getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.PROPERTIES)) {
-            final AdapterAndProperty aap = AdapterAndProperty.of(adapter, property);
-            if(property.isNotPersisted()) {
-                continue;
-            }
-            if(changedObjectProperties.containsKey(aap)) {
-                // already enlisted, so ignore
-                return;
-            }
-            PreAndPostValues papv = PreAndPostValues.pre(Placeholder.NEW);
-            changedObjectProperties.put(aap, papv);
-        }
-    }
-
-    /**
-     * Auditing and publishing support: for object stores to enlist an object that is about to be updated, 
-     * capturing the pre-modification values of the properties of the {@link ObjectAdapter}.
-     * 
-     * <p>
-     * The post-modification values are captured in {@link #preCommit()}.
-     *
-     * <p>
-     * Supported by the JDO object store; check documentation for support in other objectstores.
-     */
-    public void enlistUpdating(ObjectAdapter adapter) {
-        enlist(adapter, ChangeKind.UPDATE);
-        for (ObjectAssociation property : adapter.getSpecification().getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.PROPERTIES)) {
-            final AdapterAndProperty aap = AdapterAndProperty.of(adapter, property);
-            if(property.isNotPersisted()) {
-                continue;
-            }
-            if(changedObjectProperties.containsKey(aap)) {
-                // already enlisted, so ignore
-                return;
-            }
-            PreAndPostValues papv = PreAndPostValues.pre(aap.getPropertyValue());
-            changedObjectProperties.put(aap, papv);
-        }
-    }
-
-    /**
-     * Auditing and publishing support: for object stores to enlist an object that is about to be deleted, 
-     * capturing the pre-deletion value of the properties of the {@link ObjectAdapter}. 
-     * 
-     * <p>
-     * The post-modification values are captured in {@link #preCommit()}.  In the case of deleted objects, a
-     * dummy value <tt>'[DELETED]'</tt> is used as the post-modification value.
-     * 
-     * <p>
-     * Supported by the JDO object store; check documentation for support in other objectstores.
-     */
-    public void enlistDeleting(ObjectAdapter adapter) {
-        final boolean enlisted = enlist(adapter, ChangeKind.DELETE);
-        if(!enlisted) {
-            return;
-        }
-        for (ObjectAssociation property : adapter.getSpecification().getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.PROPERTIES)) {
-            final AdapterAndProperty aap = AdapterAndProperty.of(adapter, property);
-            if(property.isNotPersisted()) {
-                continue;
-            }
-            if(changedObjectProperties.containsKey(aap)) {
-                // already enlisted, so ignore
-                return;
-            }
-            PreAndPostValues papv = PreAndPostValues.pre(aap.getPropertyValue());
-            changedObjectProperties.put(aap, papv);
-        }
-    }
-
-
-    /**
-     *
-     * @param adapter
-     * @param current
-     * @return <code>true</code> if successfully enlisted, <code>false</code> if was already enlisted
-     */
-    private boolean enlist(final ObjectAdapter adapter, final ChangeKind current) {
-        final ChangeKind previous = changeKindByEnlistedAdapter.get(adapter);
-        if(previous == null) {
-            changeKindByEnlistedAdapter.put(adapter, current);
-            return true;
-        }
-        switch (previous) {
-            case CREATE:
-                switch (current) {
-                    case DELETE:
-                        changeKindByEnlistedAdapter.remove(adapter);
-                    case CREATE:
-                    case UPDATE:
-                        return false;
-                }
-                break;
-            case UPDATE:
-                switch (current) {
-                    case DELETE:
-                        changeKindByEnlistedAdapter.put(adapter, current);
-                        return true;
-                    case CREATE:
-                    case UPDATE:
-                        return false;
-                }
-                break;
-            case DELETE:
-                return false;
-        }
-        return previous == null;
-    }
-
 
     ////////////////////////////////////////////////////////////////////////
     // Dependencies (lookup)
@@ -1013,6 +616,4 @@ public class IsisTransaction implements TransactionScopedComponent {
     private <T> T lookupServiceIfAny(final Class<T> serviceType) {
         return servicesInjector.lookupService(serviceType);
     }
-
-
 }


[2/3] isis git commit: ISIS-1370: moved AuditingServiceInternal to an auditing package.

Posted by da...@apache.org.
ISIS-1370: moved AuditingServiceInternal to an auditing package.


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/e2fb62e6
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/e2fb62e6
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/e2fb62e6

Branch: refs/heads/ISIS-1291
Commit: e2fb62e65eebd8f9930947333d72cb73144fdce2
Parents: 5870f6a
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Sun May 1 11:33:51 2016 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Sun May 1 11:33:51 2016 +0100

----------------------------------------------------------------------
 .../auditing/AuditingServiceInternal.java       | 117 +++++++++++++++++++
 .../publishing/AuditingServiceInternal.java     | 117 -------------------
 .../system/transaction/IsisTransaction.java     |   2 +-
 .../handlers/DomainObjectInvocationHandler.java |   8 +-
 4 files changed, 122 insertions(+), 122 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/e2fb62e6/core/runtime/src/main/java/org/apache/isis/core/runtime/services/auditing/AuditingServiceInternal.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/auditing/AuditingServiceInternal.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/auditing/AuditingServiceInternal.java
new file mode 100644
index 0000000..a470946
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/auditing/AuditingServiceInternal.java
@@ -0,0 +1,117 @@
+/*
+ *  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.runtime.services.auditing;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.services.audit.AuditingService3;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.clock.ClockService;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.services.command.CommandContext;
+import org.apache.isis.applib.services.user.UserService;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facets.actions.action.invocation.CommandUtil;
+import org.apache.isis.core.metamodel.facets.object.audit.AuditableFacet;
+import org.apache.isis.core.runtime.system.transaction.IsisTransaction;
+
+/**
+ * Wrapper around {@link org.apache.isis.applib.services.audit.AuditingService3}.  Is a no-op if there is no injected service.
+ */
+@DomainService(nature = NatureOfService.DOMAIN)
+public class AuditingServiceInternal {
+
+
+    @Programmatic
+    public boolean canAudit() {
+        return auditingServiceIfAny != null;
+    }
+
+    @Programmatic
+    public void audit(
+            final Set<Map.Entry<IsisTransaction.AdapterAndProperty, IsisTransaction.PreAndPostValues>> changedObjectProperties) {
+
+        if(!canAudit()) {
+            return;
+        }
+
+        final String currentUser = userService.getUser().getName();
+        final java.sql.Timestamp currentTime = clockService.nowAsJavaSqlTimestamp();
+
+        for (Map.Entry<IsisTransaction.AdapterAndProperty, IsisTransaction.PreAndPostValues> auditEntry : changedObjectProperties) {
+            auditChangedProperty(currentTime, currentUser, auditEntry);
+        }
+
+    }
+
+    private void auditChangedProperty(
+            final java.sql.Timestamp timestamp,
+            final String user,
+            final Map.Entry<IsisTransaction.AdapterAndProperty, IsisTransaction.PreAndPostValues> auditEntry) {
+
+        final IsisTransaction.AdapterAndProperty aap = auditEntry.getKey();
+        final ObjectAdapter adapter = aap.getAdapter();
+
+        final AuditableFacet auditableFacet = adapter.getSpecification().getFacet(AuditableFacet.class);
+        if(auditableFacet == null || auditableFacet.isDisabled()) {
+            return;
+        }
+
+        final Bookmark target = aap.getBookmark();
+        final String propertyId = aap.getPropertyId();
+        final String memberId = aap.getMemberId();
+
+        final IsisTransaction.PreAndPostValues papv = auditEntry.getValue();
+        final String preValue = papv.getPreString();
+        final String postValue = papv.getPostString();
+
+
+        final String targetClass = CommandUtil.targetClassNameFor(adapter);
+
+        final Command command = commandContext.getCommand();
+        final UUID transactionId = command.getTransactionId();
+
+        auditingServiceIfAny
+                .audit(transactionId, targetClass, target, memberId, propertyId, preValue, postValue, user, timestamp);
+    }
+
+    /**
+     * could be null if none has been registered.
+     */
+    @Inject
+    private AuditingService3 auditingServiceIfAny;
+
+    @Inject
+    UserService userService;
+
+    @Inject
+    ClockService clockService;
+
+    @Inject
+    CommandContext commandContext;
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e2fb62e6/core/runtime/src/main/java/org/apache/isis/core/runtime/services/publishing/AuditingServiceInternal.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/publishing/AuditingServiceInternal.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/publishing/AuditingServiceInternal.java
deleted file mode 100644
index 3f29804..0000000
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/publishing/AuditingServiceInternal.java
+++ /dev/null
@@ -1,117 +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.runtime.services.publishing;
-
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-
-import javax.inject.Inject;
-
-import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.annotation.NatureOfService;
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.services.audit.AuditingService3;
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.clock.ClockService;
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandContext;
-import org.apache.isis.applib.services.user.UserService;
-import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.core.metamodel.facets.actions.action.invocation.CommandUtil;
-import org.apache.isis.core.metamodel.facets.object.audit.AuditableFacet;
-import org.apache.isis.core.runtime.system.transaction.IsisTransaction;
-
-/**
- * Wrapper around {@link org.apache.isis.applib.services.audit.AuditingService3}.  Is a no-op if there is no injected service.
- */
-@DomainService(nature = NatureOfService.DOMAIN)
-public class AuditingServiceInternal {
-
-
-    @Programmatic
-    public boolean canAudit() {
-        return auditingServiceIfAny != null;
-    }
-
-    @Programmatic
-    public void audit(
-            final Set<Map.Entry<IsisTransaction.AdapterAndProperty, IsisTransaction.PreAndPostValues>> changedObjectProperties) {
-
-        if(!canAudit()) {
-            return;
-        }
-
-        final String currentUser = userService.getUser().getName();
-        final java.sql.Timestamp currentTime = clockService.nowAsJavaSqlTimestamp();
-
-        for (Map.Entry<IsisTransaction.AdapterAndProperty, IsisTransaction.PreAndPostValues> auditEntry : changedObjectProperties) {
-            auditChangedProperty(currentTime, currentUser, auditEntry);
-        }
-
-    }
-
-    private void auditChangedProperty(
-            final java.sql.Timestamp timestamp,
-            final String user,
-            final Map.Entry<IsisTransaction.AdapterAndProperty, IsisTransaction.PreAndPostValues> auditEntry) {
-
-        final IsisTransaction.AdapterAndProperty aap = auditEntry.getKey();
-        final ObjectAdapter adapter = aap.getAdapter();
-
-        final AuditableFacet auditableFacet = adapter.getSpecification().getFacet(AuditableFacet.class);
-        if(auditableFacet == null || auditableFacet.isDisabled()) {
-            return;
-        }
-
-        final Bookmark target = aap.getBookmark();
-        final String propertyId = aap.getPropertyId();
-        final String memberId = aap.getMemberId();
-
-        final IsisTransaction.PreAndPostValues papv = auditEntry.getValue();
-        final String preValue = papv.getPreString();
-        final String postValue = papv.getPostString();
-
-
-        final String targetClass = CommandUtil.targetClassNameFor(adapter);
-
-        final Command command = commandContext.getCommand();
-        final UUID transactionId = command.getTransactionId();
-
-        auditingServiceIfAny
-                .audit(transactionId, targetClass, target, memberId, propertyId, preValue, postValue, user, timestamp);
-    }
-
-    /**
-     * could be null if none has been registered.
-     */
-    @Inject
-    private AuditingService3 auditingServiceIfAny;
-
-    @Inject
-    UserService userService;
-
-    @Inject
-    ClockService clockService;
-
-    @Inject
-    CommandContext commandContext;
-
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/e2fb62e6/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
index 99b3079..e49601c 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/transaction/IsisTransaction.java
@@ -66,7 +66,7 @@ import org.apache.isis.core.metamodel.transactions.TransactionState;
 import org.apache.isis.core.runtime.persistence.objectstore.transaction.CreateObjectCommand;
 import org.apache.isis.core.runtime.persistence.objectstore.transaction.DestroyObjectCommand;
 import org.apache.isis.core.runtime.persistence.objectstore.transaction.PersistenceCommand;
-import org.apache.isis.core.runtime.services.publishing.AuditingServiceInternal;
+import org.apache.isis.core.runtime.services.auditing.AuditingServiceInternal;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 
 import static org.apache.isis.core.commons.ensure.Ensure.ensureThatArg;

http://git-wip-us.apache.org/repos/asf/isis/blob/e2fb62e6/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/DomainObjectInvocationHandler.java
----------------------------------------------------------------------
diff --git a/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/DomainObjectInvocationHandler.java b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/DomainObjectInvocationHandler.java
index 023921c..18a3f26 100644
--- a/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/DomainObjectInvocationHandler.java
+++ b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/DomainObjectInvocationHandler.java
@@ -766,11 +766,11 @@ public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandle
     }
 
     private ObjectSpecificationDefault getJavaSpecification(final Class<?> clazz) {
-        final ObjectSpecification nos = getSpecification(clazz);
-        if (!(nos instanceof ObjectSpecificationDefault)) {
-            throw new UnsupportedOperationException("Only Java is supported (specification is '" + nos.getClass().getCanonicalName() + "')");
+        final ObjectSpecification objectSpec = getSpecification(clazz);
+        if (!(objectSpec instanceof ObjectSpecificationDefault)) {
+            throw new UnsupportedOperationException("Only Java is supported (specification is '" + objectSpec.getClass().getCanonicalName() + "')");
         }
-        return (ObjectSpecificationDefault) nos;
+        return (ObjectSpecificationDefault) objectSpec;
     }
 
     private ObjectSpecification getSpecification(final Class<?> type) {