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 2014/01/30 09:22:52 UTC

[5/5] git commit: ISIS-660, ISIS-661, ISIS-662, ISIS-663: background tasks and similar

ISIS-660,ISIS-661,ISIS-662,ISIS-663: background tasks and similar

ISIS-660:
- consistent pattern of services vs repository vs contributions for
  each of interactions, background tasks, published events and audit entry
- refactored todo app, introducing new Admin service that delegates
- InteractionOutcome and InteractionOutcome.Type, capture any exception
- Interaction.Nature
- HasTransaction now defines transactionId property as a UUID, not a String
- can now use UUID as a @PrimaryKey (used by the HasTransaction implementations)

ISIS-661: BackgroundTask contributions service
ISIS-662: PublishedEvent contributions service
ISIS-663: AuditEntry contributions service


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

Branch: refs/heads/master
Commit: b99640031fd05002aefa455b201eff37656f1776
Parents: dc0fb67
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Thu Jan 30 08:14:50 2014 +0000
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Thu Jan 30 08:14:50 2014 +0000

----------------------------------------------------------------------
 .../objectstore/jdo/applib/AuditService.java    |  27 --
 .../jdo/applib/AuditServiceLogging.java         |  38 ---
 .../jdo/applib/service/audit/AuditEntry.java    | 158 ------------
 .../service/audit/AuditEntryContributions.java  |  47 ++++
 .../jdo/applib/service/audit/AuditEntryJdo.java | 242 ++++++++++++++++++
 .../service/audit/AuditEntryRepository.java     |  44 ++++
 .../service/audit/AuditingServiceJdo.java       |  54 ++--
 .../background/BackgroundTaskContributions.java |  46 ++++
 .../service/background/BackgroundTaskJdo.java   |  14 +-
 .../background/BackgroundTaskRepository.java    |  47 ++++
 .../background/BackgroundTaskServiceJdo.java    |  16 +-
 .../service/interaction/InteractionJdo.java     | 256 +++++++++++++++----
 .../interaction/InteractionRepository.java      |  54 +++-
 .../interaction/InteractionServiceJdo.java      |  81 ++----
 .../applib/service/publish/PublishedEvent.java  |  22 +-
 .../publish/PublishedEventContributions.java    |  47 ++++
 .../publish/PublishedEventRepository.java       |  71 +++++
 .../service/publish/PublishingServiceJdo.java   |  46 +---
 .../persistence/spi/JdoObjectIdSerializer.java  |  53 ++--
 .../ui/components/actions/ActionPanel.java      |  35 ++-
 .../apache/isis/applib/annotation/Audited.java  |   4 +-
 .../apache/isis/applib/marker/Auditable.java    |   5 +-
 .../isis/applib/services/HasTransactionId.java  |   6 +-
 .../applib/services/audit/AuditingService.java  |  53 ----
 .../applib/services/audit/AuditingService2.java |  41 ---
 .../applib/services/audit/AuditingService3.java |  43 ++++
 .../background/BackgroundTaskService.java       |   4 +-
 .../services/interaction/Interaction.java       |  64 +++++
 .../interaction/InteractionDefault.java         |  62 ++++-
 .../interaction/spi/InteractionFactory.java     |  12 +-
 .../bookmarks/BookmarkServiceDefault.java       |   3 +
 .../invoke/ActionInvocationFacetViaMethod.java  |  55 ++--
 .../background/BackgroundServiceDefault.java    |   3 +-
 .../system/persistence/PersistenceSession.java  |   5 +-
 .../system/transaction/IsisTransaction.java     |  44 ++--
 .../transaction/IsisTransactionManager.java     |  10 +-
 .../PersistenceSessionObjectStoreTest.java      |   6 +-
 .../BackgroundServiceDefaultTest_execute.java   |   9 +-
 .../system/transaction/IsisTransactionTest.java |   8 +-
 .../dom/src/main/java/dom/todo/ToDoItem.java    |  32 ++-
 .../src/main/java/webapp/admin/Admin.java       | 140 ++++++++++
 .../java/webapp/background/BackgroundTasks.java |  45 ----
 .../java/webapp/background/Interactions.java    |  53 ----
 .../src/main/webapp/WEB-INF/isis.properties     |  53 ++--
 44 files changed, 1410 insertions(+), 748 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/AuditService.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/AuditService.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/AuditService.java
deleted file mode 100644
index d823658..0000000
--- a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/AuditService.java
+++ /dev/null
@@ -1,27 +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.objectstore.jdo.applib;
-
-
-/**
- * @deprecated use the {@link org.apache.isis.applib.services.audit.AuditingService the applib AuditService} instead.
- */
-@Deprecated
-public interface AuditService extends org.apache.isis.applib.services.audit.AuditingService {
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/AuditServiceLogging.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/AuditServiceLogging.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/AuditServiceLogging.java
deleted file mode 100644
index cdf1dde..0000000
--- a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/AuditServiceLogging.java
+++ /dev/null
@@ -1,38 +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.objectstore.jdo.applib;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.isis.applib.annotation.Hidden;
-
-
-@SuppressWarnings("deprecation")
-public class AuditServiceLogging implements AuditService {
-
-    private final static Logger LOG = LoggerFactory.getLogger(AuditServiceLogging.class);
-    
-    @Hidden
-    public void audit(String user, long currentTimestampEpoch, String objectType, String identifier, String preValue, String postValue) {
-        String auditMessage = objectType + ":" + identifier + " by " + user + ": " + preValue + " -> " + postValue;
-        LOG.info(auditMessage);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntry.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntry.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntry.java
deleted file mode 100644
index ea269f9..0000000
--- a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntry.java
+++ /dev/null
@@ -1,158 +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.objectstore.jdo.applib.service.audit;
-
-import javax.jdo.annotations.IdGeneratorStrategy;
-import javax.jdo.annotations.IdentityType;
-
-import org.apache.isis.applib.annotation.Hidden;
-import org.apache.isis.applib.annotation.Immutable;
-import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.annotation.Title;
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.bookmark.BookmarkHolder;
-import org.apache.isis.applib.value.DateTime;
-
-@javax.jdo.annotations.PersistenceCapable(
-        identityType=IdentityType.DATASTORE,
-        table="IsisAuditEntry")
-@javax.jdo.annotations.DatastoreIdentity(strategy=IdGeneratorStrategy.UUIDHEX)
-@Immutable
-public class AuditEntry implements BookmarkHolder {
-
-    // //////////////////////////////////////
-
-    private Long timestampEpoch;
-
-    @javax.jdo.annotations.Column(allowsNull="false")
-    @Hidden
-    public Long getTimestampEpoch() {
-        return timestampEpoch;
-    }
-
-    public void setTimestampEpoch(final Long timestampEpoch) {
-        this.timestampEpoch = timestampEpoch;
-    }
-
-    // //////////////////////////////////////
-
-    @Title(sequence="1")
-    @MemberOrder(sequence = "1")
-    public DateTime getTimestamp() {
-        return timestampEpoch != null? new DateTime(timestampEpoch): null;
-    }
-
-    // //////////////////////////////////////
-
-    private String user;
-
-    @javax.jdo.annotations.Column(allowsNull="false")
-    @Title(sequence="2", prepend=",")
-    @MemberOrder(sequence = "2")
-    public String getUser() {
-        return user;
-    }
-
-    public void setUser(final String user) {
-        this.user = user;
-    }
-    
-    // //////////////////////////////////////
-
-    private String objectType;
-
-    @javax.jdo.annotations.Column(allowsNull="false")
-    @Title(sequence="3", prepend=" ")
-    @MemberOrder(sequence = "3")
-    public String getObjectType() {
-        return objectType;
-    }
-
-    public void setObjectType(final String objectType) {
-        this.objectType = objectType;
-    }
-    
-    // //////////////////////////////////////
-
-    private String identifier;
-
-    @javax.jdo.annotations.Column(allowsNull="false")
-    @Title(sequence="4", prepend=":")
-    @MemberOrder(sequence = "4")
-    public String getIdentifier() {
-        return identifier;
-    }
-
-    public void setIdentifier(final String identifier) {
-        this.identifier = identifier;
-    }
-    
-    // //////////////////////////////////////
-    
-    private String propertyId;
-    
-    @javax.jdo.annotations.Column(allowsNull="true")
-    @Title(sequence="5", prepend=", ")
-    @MemberOrder(sequence = "5")
-    public String getPropertyId() {
-        return propertyId;
-    }
-    
-    public void setPropertyId(final String propertyId) {
-        this.propertyId = propertyId;
-    }
-    
-    
-    // //////////////////////////////////////
-
-    private String preValue;
-
-    @javax.jdo.annotations.Column(allowsNull="true")
-    @MemberOrder(sequence = "5")
-    public String getPreValue() {
-        return preValue;
-    }
-
-    public void setPreValue(final String preValue) {
-        this.preValue = preValue;
-    }
-    
-    // //////////////////////////////////////
-
-    private String postValue;
-
-    @javax.jdo.annotations.Column(allowsNull="true")
-    @MemberOrder(sequence = "6")
-    public String getPostValue() {
-        return postValue;
-    }
-
-    public void setPostValue(final String postValue) {
-        this.postValue = postValue;
-    }
-    
-    // //////////////////////////////////////
-
-    @Override
-    @Programmatic
-    public Bookmark bookmark() {
-        return new Bookmark(getObjectType(), getIdentifier());
-    }
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntryContributions.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntryContributions.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntryContributions.java
new file mode 100644
index 0000000..8db6c42
--- /dev/null
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntryContributions.java
@@ -0,0 +1,47 @@
+/**
+ *  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.objectstore.jdo.applib.service.audit;
+
+import java.util.List;
+
+import org.apache.isis.applib.AbstractFactoryAndRepository;
+import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.Render;
+import org.apache.isis.applib.annotation.ActionSemantics.Of;
+import org.apache.isis.applib.annotation.NotContributed;
+import org.apache.isis.applib.annotation.NotContributed.As;
+import org.apache.isis.applib.annotation.Render.Type;
+import org.apache.isis.applib.annotation.NotInServiceMenu;
+import org.apache.isis.applib.services.HasTransactionId;
+
+
+public class AuditEntryContributions extends AbstractFactoryAndRepository {
+
+    @ActionSemantics(Of.SAFE)
+    @NotInServiceMenu
+    @NotContributed(As.ACTION)
+    @Render(Type.EAGERLY)
+    public List<AuditEntryJdo> auditEntries(final HasTransactionId hasTransactionId) {
+        return auditEntryRepository.findByTransactionId(hasTransactionId.getTransactionId());
+    }
+    
+    // //////////////////////////////////////
+
+    @javax.inject.Inject
+    private AuditEntryRepository auditEntryRepository;
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntryJdo.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntryJdo.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntryJdo.java
new file mode 100644
index 0000000..baa739f
--- /dev/null
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntryJdo.java
@@ -0,0 +1,242 @@
+/*
+ *  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.objectstore.jdo.applib.service.audit;
+
+import java.sql.Timestamp;
+import java.text.SimpleDateFormat;
+import java.util.UUID;
+
+import javax.jdo.annotations.IdentityType;
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.ActionSemantics.Of;
+import org.apache.isis.applib.annotation.Disabled;
+import org.apache.isis.applib.annotation.Hidden;
+import org.apache.isis.applib.annotation.Immutable;
+import org.apache.isis.applib.annotation.MemberGroupLayout;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.Named;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.annotation.TypicalLength;
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.applib.services.HasTransactionId;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.services.interaction.Interaction;
+import org.apache.isis.applib.util.TitleBuffer;
+
+@javax.jdo.annotations.PersistenceCapable(
+        identityType=IdentityType.DATASTORE,
+        table="IsisAuditEntry")
+@javax.jdo.annotations.DatastoreIdentity(
+        strategy=javax.jdo.annotations.IdGeneratorStrategy.IDENTITY,
+        column="id")
+@javax.jdo.annotations.Queries( {
+    @javax.jdo.annotations.Query(
+            name="findByTransactionId", language="JDOQL",  
+            value="SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.audit.AuditEntry "
+                    + "WHERE transactionId == :transactionId")
+})
+@Immutable
+@Named("Audit Entry")
+@MemberGroupLayout(left={"When/Who","Target","Values"})
+public class AuditEntryJdo implements HasTransactionId {
+
+    
+    public String title() {
+        final TitleBuffer buf = new TitleBuffer();
+        buf.append(
+        new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(getTimestamp()));
+        buf.append(",", getUser());
+        buf.append(":", getPropertyId());
+        return buf.toString();
+    }
+    
+    
+    // //////////////////////////////////////
+
+    private Timestamp timestamp;
+
+    @javax.jdo.annotations.Column(allowsNull="false")
+    @MemberOrder(name="When/Who",sequence = "1")
+    public Timestamp getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(final Timestamp timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    
+    // //////////////////////////////////////
+    
+    private UUID transactionId;
+
+    /**
+     * The unique identifier (a GUID) of the transaction of the {@link Interaction} that gave rise to this
+     * audit entry.
+     */
+    @javax.jdo.annotations.Column(allowsNull="true", length=36)
+    @TypicalLength(36)
+    @MemberOrder(name="When/Who",sequence = "20")
+    @Disabled
+    @Override
+    public UUID getTransactionId() {
+        return transactionId;
+    }
+
+    @Override
+    public void setTransactionId(final UUID transactionId) {
+        this.transactionId = transactionId;
+    }
+
+    // //////////////////////////////////////
+
+    
+    private String user;
+
+    @javax.jdo.annotations.Column(allowsNull="false", length=50)
+    @MemberOrder(name="When/Who",sequence = "2")
+    public String getUser() {
+        return user;
+    }
+
+    public void setUser(final String user) {
+        this.user = abbreviated(user, 50);
+    }
+    
+    
+    // //////////////////////////////////////
+    // target (property)
+    // //////////////////////////////////////
+
+    @Programmatic
+    public Bookmark getTarget() {
+        return new Bookmark(getTargetStr());
+    }
+    
+    @Programmatic
+    public void setTarget(Bookmark target) {
+        setTargetStr(target.toString());
+    }
+
+    private String targetStr;
+
+    @javax.jdo.annotations.Column(allowsNull="false", length=255, name="target")
+    @Named("Target Bookmark")
+    @Hidden(where=Where.ALL_TABLES)
+    @MemberOrder(name="Target", sequence="3")
+    public String getTargetStr() {
+        return targetStr;
+    }
+
+    public void setTargetStr(final String targetStr) {
+        this.targetStr = abbreviated(targetStr, 255);
+    }
+
+    
+    // //////////////////////////////////////
+    // openTargetObject (action)
+    // //////////////////////////////////////
+
+    @ActionSemantics(Of.SAFE)
+    @MemberOrder(name="TargetStr", sequence="1")
+    @Named("Open")
+    public Object openTargetObject() {
+        return lookupBookmark(getTarget());
+    }
+    public boolean hideOpenTargetObject() {
+        return getTargetStr() == null;
+    }
+    
+
+    // //////////////////////////////////////
+    
+    private String propertyId;
+    
+    @javax.jdo.annotations.Column(allowsNull="true", length=50)
+    @MemberOrder(name="Target",sequence = "5")
+    public String getPropertyId() {
+        return propertyId;
+    }
+    
+    public void setPropertyId(final String propertyId) {
+        this.propertyId = abbreviated(propertyId,50);
+    }
+    
+    
+    // //////////////////////////////////////
+
+    private String preValue;
+
+    @javax.jdo.annotations.Column(allowsNull="true", length=255)
+    @MemberOrder(name="Values",sequence = "6")
+    public String getPreValue() {
+        return preValue;
+    }
+
+    public void setPreValue(final String preValue) {
+        this.preValue = abbreviated(preValue,255);
+    }
+    
+    // //////////////////////////////////////
+
+    private String postValue;
+
+    @javax.jdo.annotations.Column(allowsNull="true", length=255)
+    @MemberOrder(name="Values",sequence = "7")
+    public String getPostValue() {
+        return postValue;
+    }
+
+    public void setPostValue(final String postValue) {
+        this.postValue = abbreviated(postValue, 255);
+    }
+    
+    // //////////////////////////////////////
+
+    private Object lookupBookmark(Bookmark bookmark) {
+        try {
+        return bookmarkService != null
+                ? bookmarkService.lookup(bookmark)
+                : null;
+        } catch(RuntimeException ex) {
+            if(ex.getClass().getName().contains("ObjectNotFoundException")) {
+                container.warnUser("Object not found - has it since been deleted?");
+                return null;
+            } 
+            throw ex;
+        }
+    }
+
+    private static String abbreviated(final String str, final int maxLength) {
+        return str != null? (str.length() < maxLength ? str : str.substring(0, maxLength - 3) + "..."): null;
+    }
+    
+    // //////////////////////////////////////
+
+
+    @javax.inject.Inject
+    private BookmarkService bookmarkService;
+
+    @javax.inject.Inject
+    private DomainObjectContainer container;
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntryRepository.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntryRepository.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntryRepository.java
new file mode 100644
index 0000000..f561326
--- /dev/null
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditEntryRepository.java
@@ -0,0 +1,44 @@
+/*
+ *  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.objectstore.jdo.applib.service.audit;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.isis.applib.AbstractFactoryAndRepository;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.query.QueryDefault;
+
+public class AuditEntryRepository extends AbstractFactoryAndRepository {
+    
+    @Programmatic
+    public List<AuditEntryJdo> listAll() {
+        return allInstances(AuditEntryJdo.class);
+    }
+    
+    @Programmatic
+    public List<AuditEntryJdo> findByTransactionId(final UUID transactionId) {
+        return allMatches(
+                new QueryDefault<AuditEntryJdo>(AuditEntryJdo.class, 
+                        "findByTransactionId", 
+                        "transactionId", transactionId));
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditingServiceJdo.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditingServiceJdo.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditingServiceJdo.java
index 5868c50..50a59c8 100644
--- a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditingServiceJdo.java
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/audit/AuditingServiceJdo.java
@@ -18,46 +18,38 @@
  */
 package org.apache.isis.objectstore.jdo.applib.service.audit;
 
-import java.util.List;
-
 import org.apache.isis.applib.AbstractFactoryAndRepository;
-import org.apache.isis.applib.annotation.ActionSemantics;
-import org.apache.isis.applib.annotation.ActionSemantics.Of;
-import org.apache.isis.applib.annotation.Named;
 import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.services.audit.AuditingService2;
+import org.apache.isis.applib.services.HasTransactionId;
+import org.apache.isis.applib.services.audit.AuditingService3;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.interaction.Interaction;
+import org.apache.isis.applib.services.interaction.InteractionContext;
 
-@Named("Auditing")
-public class AuditingServiceJdo extends AbstractFactoryAndRepository implements AuditingService2 {
-    
-    @ActionSemantics(Of.SAFE)
-    public List<AuditEntry> list() {
-        return allInstances(AuditEntry.class);
-    }
-
-    /**
-     * This method will never be called by Isis because the service implements, instead, {@link AuditingService2}.
-     * 
-     * @deprecated
-     */
-    @Deprecated
-    @Programmatic
-    public void audit(String user, long currentTimestampEpoch, String objectType, String identifier, String preValue, String postValue) {
-        audit(user, currentTimestampEpoch, objectType, identifier, null, preValue, postValue);
-    }
+public class AuditingServiceJdo extends AbstractFactoryAndRepository implements AuditingService3 {
 
     @Programmatic
-    @Override
-    public void audit(String user, long currentTimestampEpoch, String objectType, String identifier, String propertyId, String preValue, String postValue) {
-        AuditEntry auditEntry = newTransientInstance(AuditEntry.class);
-        auditEntry.setTimestampEpoch(currentTimestampEpoch);
+    public void audit(java.sql.Timestamp timestamp, String user, Bookmark target, String propertyId, String preValue, String postValue) {
+        AuditEntryJdo auditEntry = newTransientInstance(AuditEntryJdo.class);
+        auditEntry.setTimestamp(timestamp);
         auditEntry.setUser(user);
-        auditEntry.setObjectType(objectType);
+        Interaction interaction = this.interactionContext.getInteraction();
+        if(interaction instanceof HasTransactionId) {
+            HasTransactionId hasTransactionId = (HasTransactionId) interaction;
+            auditEntry.setTransactionId(hasTransactionId.getTransactionId());
+        }
+        auditEntry.setTarget(target);
         auditEntry.setPropertyId(propertyId);
-        auditEntry.setIdentifier(identifier);
         auditEntry.setPreValue(preValue);
         auditEntry.setPostValue(postValue);
-        persist(auditEntry);
+        persistIfNotAlready(auditEntry);
     }
 
+    
+    // //////////////////////////////////////
+
+    
+    @javax.inject.Inject
+    private InteractionContext interactionContext;
+    
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskContributions.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskContributions.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskContributions.java
new file mode 100644
index 0000000..2461fe7
--- /dev/null
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskContributions.java
@@ -0,0 +1,46 @@
+/**
+ *  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.objectstore.jdo.applib.service.background;
+
+import java.util.List;
+
+import org.apache.isis.applib.AbstractFactoryAndRepository;
+import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.Render;
+import org.apache.isis.applib.annotation.ActionSemantics.Of;
+import org.apache.isis.applib.annotation.NotContributed;
+import org.apache.isis.applib.annotation.NotContributed.As;
+import org.apache.isis.applib.annotation.NotInServiceMenu;
+import org.apache.isis.applib.annotation.Render.Type;
+import org.apache.isis.applib.services.HasTransactionId;
+
+
+public class BackgroundTaskContributions extends AbstractFactoryAndRepository {
+
+    @ActionSemantics(Of.SAFE)
+    @NotInServiceMenu
+    @NotContributed(As.ACTION)
+    @Render(Type.EAGERLY)
+    public List<BackgroundTaskJdo> backgroundTasks(final HasTransactionId hasTransactionId) {
+        return backgroundTaskRepository.findByTransactionId(hasTransactionId.getTransactionId());
+    }
+
+    // //////////////////////////////////////
+
+    @javax.inject.Inject
+    private BackgroundTaskRepository backgroundTaskRepository;
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskJdo.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskJdo.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskJdo.java
index 843f356..ca67b5b 100644
--- a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskJdo.java
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskJdo.java
@@ -19,6 +19,7 @@ package org.apache.isis.objectstore.jdo.applib.service.background;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.sql.Timestamp;
+import java.util.UUID;
 
 import javax.inject.Inject;
 import javax.jdo.annotations.IdentityType;
@@ -52,6 +53,13 @@ import org.apache.isis.applib.util.ObjectContracts;
 @javax.jdo.annotations.DatastoreIdentity(
         strategy=javax.jdo.annotations.IdGeneratorStrategy.IDENTITY,
          column="id")
+@javax.jdo.annotations.Queries( {
+    @javax.jdo.annotations.Query(
+            name="findByTransactionId", language="JDOQL",  
+            value="SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.background.BackgroundTaskJdo "
+                    + "WHERE transactionId == :transactionId")
+})
 @Named("Background Task")
 @MemberGroupLayout(
         columnSpans={6,0,6}, 
@@ -269,7 +277,7 @@ public class BackgroundTaskJdo implements HasTransactionId {
     // //////////////////////////////////////
 
     
-    private String transactionId;
+    private UUID transactionId;
 
     /**
      * The unique identifier (a GUID) of the transaction of the {@link Interaction} that gave rise to this
@@ -281,12 +289,12 @@ public class BackgroundTaskJdo implements HasTransactionId {
     @MemberOrder(name="Identifiers",sequence = "20")
     @Disabled
     @Override
-    public String getTransactionId() {
+    public UUID getTransactionId() {
         return transactionId;
     }
 
     @Override
-    public void setTransactionId(final String transactionId) {
+    public void setTransactionId(final UUID transactionId) {
         this.transactionId = transactionId;
     }
 

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskRepository.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskRepository.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskRepository.java
new file mode 100644
index 0000000..f18a670
--- /dev/null
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskRepository.java
@@ -0,0 +1,47 @@
+/**
+ *  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.objectstore.jdo.applib.service.background;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.isis.applib.AbstractFactoryAndRepository;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.query.QueryDefault;
+
+public class BackgroundTaskRepository extends AbstractFactoryAndRepository {
+
+    @SuppressWarnings("unused")
+    private static final Logger LOG = LoggerFactory.getLogger(BackgroundTaskRepository.class);
+
+    @Programmatic
+    public List<BackgroundTaskJdo> listAll() {
+        return allInstances(BackgroundTaskJdo.class);
+    }
+
+    @Programmatic
+    public List<BackgroundTaskJdo> findByTransactionId(final UUID transactionId) {
+        return allMatches(
+                new QueryDefault<BackgroundTaskJdo>(BackgroundTaskJdo.class, 
+                        "findByTransactionId", 
+                        "transactionId", transactionId));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskServiceJdo.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskServiceJdo.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskServiceJdo.java
index 9395f23..c16e048 100644
--- a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskServiceJdo.java
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/background/BackgroundTaskServiceJdo.java
@@ -16,18 +16,14 @@
  */
 package org.apache.isis.objectstore.jdo.applib.service.background;
 
-import java.util.List;
+import java.util.UUID;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.isis.applib.AbstractService;
-import org.apache.isis.applib.annotation.ActionSemantics;
-import org.apache.isis.applib.annotation.ActionSemantics.Of;
-import org.apache.isis.applib.annotation.Bookmarkable;
 import org.apache.isis.applib.annotation.Named;
 import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.annotation.Prototype;
 import org.apache.isis.applib.clock.Clock;
 import org.apache.isis.applib.services.background.ActionInvocationMemento;
 import org.apache.isis.applib.services.background.BackgroundTaskService;
@@ -40,7 +36,7 @@ public class BackgroundTaskServiceJdo extends AbstractService implements Backgro
 
     @Programmatic
     @Override
-    public void execute(final ActionInvocationMemento aim, final String transactionId) {
+    public void execute(final ActionInvocationMemento aim, final UUID transactionId) {
         final BackgroundTaskJdo backgroundTask = newTransientInstance(BackgroundTaskJdo.class);
 
         backgroundTask.setActionIdentifier(aim.getActionId());
@@ -53,12 +49,4 @@ public class BackgroundTaskServiceJdo extends AbstractService implements Backgro
         persist(backgroundTask);
     }
 
-    @Bookmarkable
-    @ActionSemantics(Of.SAFE)
-    @Prototype
-    public List<BackgroundTaskJdo> allTasks() {
-        return allInstances(BackgroundTaskJdo.class);
-    }
-    
-
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionJdo.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionJdo.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionJdo.java
index 71a0789..2d660f3 100644
--- a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionJdo.java
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionJdo.java
@@ -21,16 +21,14 @@ import java.math.RoundingMode;
 import java.sql.Timestamp;
 import java.util.UUID;
 
-import javax.inject.Inject;
 import javax.jdo.annotations.IdentityType;
-import javax.validation.constraints.Digits;
 
-import org.joda.time.DateTime;
-import org.joda.time.Seconds;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import org.apache.isis.applib.DomainObjectContainer;
 import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.ActionSemantics.Of;
 import org.apache.isis.applib.annotation.Disabled;
 import org.apache.isis.applib.annotation.Hidden;
 import org.apache.isis.applib.annotation.MemberGroupLayout;
@@ -41,12 +39,11 @@ import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.annotation.Title;
 import org.apache.isis.applib.annotation.TypicalLength;
 import org.apache.isis.applib.annotation.Where;
-import org.apache.isis.applib.annotation.ActionSemantics.Of;
 import org.apache.isis.applib.services.HasTransactionId;
 import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.bookmark.BookmarkHolder;
 import org.apache.isis.applib.services.bookmark.BookmarkService;
 import org.apache.isis.applib.services.interaction.Interaction;
+import org.apache.isis.applib.services.interaction.spi.InteractionFactory;
 import org.apache.isis.applib.util.ObjectContracts;
 
 
@@ -75,7 +72,7 @@ import org.apache.isis.applib.util.ObjectContracts;
 @MemberGroupLayout(
         columnSpans={6,0,6}, 
         left={"Target","Notes"}, 
-        right={"Identifiers","Timings"})
+        right={"Identifiers","Timings","Results"})
 @Named("Interaction")
 public class InteractionJdo implements Interaction, HasTransactionId {
 
@@ -99,7 +96,7 @@ public class InteractionJdo implements Interaction, HasTransactionId {
     }
 
     public void setActionIdentifier(final String actionIdentifier) {
-        this.actionIdentifier = actionIdentifier;
+        this.actionIdentifier = abbreviated(actionIdentifier, 255);
     }
 
     // //////////////////////////////////////
@@ -117,10 +114,12 @@ public class InteractionJdo implements Interaction, HasTransactionId {
     }
 
     public void setTargetClass(final String targetClass) {
-        this.targetClass = targetClass;
+        this.targetClass = abbreviated(targetClass, 50);
     }
 
 
+
+
     // //////////////////////////////////////
     // targetAction (property)
     // //////////////////////////////////////
@@ -136,43 +135,11 @@ public class InteractionJdo implements Interaction, HasTransactionId {
     }
     
     public void setTargetAction(final String targetAction) {
-        this.targetAction = targetAction;
-    }
-    
-    
-    // //////////////////////////////////////
-    // targetObject (derived property)
-    // //////////////////////////////////////
-
-    @ActionSemantics(Of.SAFE)
-    @MemberOrder(name="Target", sequence="3")
-    @Named("Object")
-    public Object getTargetObject() {
-        return bookmarkService.lookup(getTarget());
+        this.targetAction = abbreviated(targetAction, 50);
     }
     
 
     // //////////////////////////////////////
-    // arguments (property)
-    // //////////////////////////////////////
-    
-    private String arguments;
-    
-    @javax.jdo.annotations.Column(allowsNull="false", length=255)
-    @MultiLine(numberOfLines=6)
-    @Hidden(where=Where.ALL_TABLES)
-    @MemberOrder(name="Target",sequence = "4")
-    @Disabled
-    public String getArguments() {
-        return arguments;
-    }
-    
-    public void setArguments(final String arguments) {
-        this.arguments = arguments;
-    }
-    
-    
-    // //////////////////////////////////////
     // target (property)
     // //////////////////////////////////////
 
@@ -190,13 +157,50 @@ public class InteractionJdo implements Interaction, HasTransactionId {
 
     private String targetStr;
     @javax.jdo.annotations.Column(allowsNull="false", length=255, name="target")
-    @Hidden
+    @Named("Target Bookmark")
+    @Hidden(where=Where.ALL_TABLES)
+    @MemberOrder(name="Target", sequence="3")
     public String getTargetStr() {
         return targetStr;
     }
 
     public void setTargetStr(final String targetStr) {
-        this.targetStr = targetStr;
+        this.targetStr = abbreviated(targetStr, 255);
+    }
+
+    
+    // //////////////////////////////////////
+    // openTargetObject (action)
+    // //////////////////////////////////////
+
+    @ActionSemantics(Of.SAFE)
+    @MemberOrder(name="TargetStr", sequence="1")
+    @Named("Open")
+    public Object openTargetObject() {
+        return lookupBookmark(getTarget());
+    }
+    public boolean hideOpenTargetObject() {
+        return getTargetStr() == null;
+    }
+    
+
+    // //////////////////////////////////////
+    // arguments (property)
+    // //////////////////////////////////////
+    
+    private String arguments;
+    
+    @javax.jdo.annotations.Column(allowsNull="false", length=1024)
+    @MultiLine(numberOfLines=6)
+    @Hidden(where=Where.ALL_TABLES)
+    @MemberOrder(name="Target",sequence = "4")
+    @Disabled
+    public String getArguments() {
+        return arguments;
+    }
+    
+    public void setArguments(final String arguments) {
+        this.arguments = abbreviated(arguments, 1024);
     }
 
     // //////////////////////////////////////
@@ -258,7 +262,7 @@ public class InteractionJdo implements Interaction, HasTransactionId {
     }
 
     public void setUser(final String user) {
-        this.user = user;
+        this.user = abbreviated(user,50);
     }
 
 
@@ -276,7 +280,7 @@ public class InteractionJdo implements Interaction, HasTransactionId {
      * Not part of the applib API, because the default implementation is not persistent
      * and so there's no object that can be accessed to be annotated.
      */
-    @javax.jdo.annotations.Column(allowsNull="true", length=255)
+    @javax.jdo.annotations.Column(allowsNull="true", length=1024)
     @MultiLine(numberOfLines=10)
     @Hidden(where=Where.ALL_TABLES)
     @MemberOrder(name="Notes", sequence = "6")
@@ -285,7 +289,7 @@ public class InteractionJdo implements Interaction, HasTransactionId {
     }
 
     public void setNotes(final String notes) {
-        this.notes = notes;
+        this.notes = abbreviated(notes, 1024);
     }
 
 
@@ -315,10 +319,8 @@ public class InteractionJdo implements Interaction, HasTransactionId {
     // transactionId (property)
     // //////////////////////////////////////
 
-    
-    // //////////////////////////////////////
-    
-    private String transactionId;
+        
+    private UUID transactionId;
 
     /**
      * The unique identifier (a GUID) of the transaction in which this interaction occurred.
@@ -329,7 +331,7 @@ public class InteractionJdo implements Interaction, HasTransactionId {
     @MemberOrder(name="Identifiers",sequence = "20")
     @Disabled
     @Override
-    public String getTransactionId() {
+    public UUID getTransactionId() {
         return transactionId;
     }
 
@@ -340,11 +342,133 @@ public class InteractionJdo implements Interaction, HasTransactionId {
      * Implementation notes: copied over from the Isis transaction when the interaction is persisted.
      */
     @Override
-    public void setTransactionId(final String transactionId) {
+    public void setTransactionId(final UUID transactionId) {
         this.transactionId = transactionId;
     }
 
     // //////////////////////////////////////
+    // nature (property)
+    // //////////////////////////////////////
+
+    private Nature nature;
+
+    @javax.jdo.annotations.NotPersistent
+    @Programmatic
+    @Override
+    public Nature getNature() {
+        return nature;
+    }
+    
+    /**
+     * <b>NOT API</b>: intended to be called only by the framework.
+     * 
+     * <p>
+     * Implementation notes: populated by the viewer as hint to {@link InteractionFactory} implementation.
+     */
+    @Override
+    public void setNature(Nature nature) {
+        this.nature = nature;
+    }
+
+    
+    // //////////////////////////////////////
+    // result (property)
+    // //////////////////////////////////////
+
+    @Programmatic
+    @Override
+    public Bookmark getResult() {
+        return getResultStr() != null? new Bookmark(getResultStr()): null;
+    }
+    
+    @Programmatic
+    @Override
+    public void setResult(Bookmark result) {
+        setResultStr(result != null? result.toString(): null);
+    }
+
+    private String resultStr;
+    @javax.jdo.annotations.Column(allowsNull="true", length=255, name="result")
+    @Hidden(where=Where.ALL_TABLES)
+    @Named("Result Bookmark")
+    @MemberOrder(name="Results", sequence="25")
+    public String getResultStr() {
+        return resultStr;
+    }
+
+    public void setResultStr(final String resultStr) {
+        this.resultStr = abbreviated(resultStr,255);
+    }
+
+    
+    // //////////////////////////////////////
+    // openResultObject (action)
+    // //////////////////////////////////////
+
+    @ActionSemantics(Of.SAFE)
+    @MemberOrder(name="ResultStr", sequence="1")
+    @Named("Open")
+    public Object openResultObject() {
+        Bookmark bookmark = getResult();
+        return lookupBookmark(bookmark);
+    }
+    public boolean hideOpenResultObject() {
+        return getResultStr() == null;
+    }
+
+
+
+    // //////////////////////////////////////
+    // exception (property)
+    // //////////////////////////////////////
+
+    private String exception;
+
+    /**
+     * Stack trace of any exception that might have occurred if this interaction/transaction aborted.
+     * 
+     * <p>
+     * Not visible in the UI, but accessible 
+     * <p>
+     * Not part of the applib API, because the default implementation is not persistent
+     * and so there's no object that can be accessed to be annotated.
+     */
+    @javax.jdo.annotations.Column(allowsNull="true", length=2000)
+    @Hidden
+    @Override
+    public String getException() {
+        return exception;
+    }
+
+    @Override
+    public void setException(final String exception) {
+        this.exception = abbreviated(exception, 2000);
+    }
+    
+    
+    // //////////////////////////////////////
+    // causedException (derived property)
+    // showException (associated action)
+    // //////////////////////////////////////
+    
+    @javax.jdo.annotations.NotPersistent
+    @MemberOrder(name="Results",sequence = "30")
+    @Hidden(where=Where.ALL_TABLES)
+    public boolean isCausedException() {
+        return getException() != null;
+    }
+
+    @ActionSemantics(Of.SAFE)
+    @MemberOrder(name="causedException", sequence = "1")
+    public String showException() {
+        return getException();
+    }
+    public boolean hideShowException() {
+        return !isCausedException();
+    }
+
+    // //////////////////////////////////////
+
 
     @Override
     public String toString() {
@@ -352,8 +476,30 @@ public class InteractionJdo implements Interaction, HasTransactionId {
     }
 
     // //////////////////////////////////////
+
+    private Object lookupBookmark(Bookmark bookmark) {
+        try {
+        return bookmarkService != null
+                ? bookmarkService.lookup(bookmark)
+                : null;
+        } catch(RuntimeException ex) {
+            if(ex.getClass().getName().contains("ObjectNotFoundException")) {
+                container.warnUser("Object not found - has it since been deleted?");
+                return null;
+            } 
+            throw ex;
+        }
+    }
+
+    private static String abbreviated(final String str, final int maxLength) {
+        return str != null? (str.length() < maxLength ? str : str.substring(0, maxLength - 3) + "..."): null;
+    }
+
+    // //////////////////////////////////////
     
-    @Inject
+    @javax.inject.Inject
     private BookmarkService bookmarkService;
-
+    
+    @javax.inject.Inject
+    private DomainObjectContainer container;
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionRepository.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionRepository.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionRepository.java
index 6b95f3f..8fd209b 100644
--- a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionRepository.java
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionRepository.java
@@ -19,18 +19,54 @@ package org.apache.isis.objectstore.jdo.applib.service.interaction;
 import java.util.List;
 import java.util.UUID;
 
-import org.apache.isis.applib.annotation.ActionSemantics;
-import org.apache.isis.applib.annotation.ActionSemantics.Of;
+import org.apache.isis.applib.AbstractFactoryAndRepository;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.query.QueryDefault;
+import org.apache.isis.applib.services.interaction.Interaction;
+import org.apache.isis.applib.services.interaction.InteractionContext;
 
 
-public interface InteractionRepository {
+public class InteractionRepository extends AbstractFactoryAndRepository {
+
+    @Programmatic
+    public InteractionJdo findByTransactionId(final UUID transactionId) {
+        persistCurrentInteractionIfRequired();
+        return firstMatch(
+                new QueryDefault<InteractionJdo>(InteractionJdo.class, 
+                        "findByTransactionId", 
+                        "transactionId", transactionId.toString()));
+    }
+
+    private void persistCurrentInteractionIfRequired() {
+        if(interactionContext != null && interactionService != null) {
+            Interaction interaction = interactionContext.getInteraction();
+            final InteractionJdo interactionJdo = interactionService.asPersistableInteractionJdo(interaction);
+            persistIfNotAlready(interactionJdo);
+        }
+    }
 
-    @ActionSemantics(Of.SAFE)
-    InteractionJdo findByTransactionId(final UUID transactionId);
     
-    @ActionSemantics(Of.SAFE)
-    List<InteractionJdo> currentInteractions();
+    @Programmatic
+    public List<InteractionJdo> findCurrent() {
+        persistCurrentInteractionIfRequired();
+        return allMatches(
+                new QueryDefault<InteractionJdo>(InteractionJdo.class, "findCurrent"));
+    }
     
-    @ActionSemantics(Of.SAFE)
-    List<InteractionJdo> completedInteractions();
+    @Programmatic
+    public List<InteractionJdo> findCompleted() {
+        persistCurrentInteractionIfRequired();
+        return allMatches(
+                new QueryDefault<InteractionJdo>(InteractionJdo.class, "findCompleted"));
+    }
+
+    // //////////////////////////////////////
+
+    
+    @javax.inject.Inject
+    private InteractionServiceJdo interactionService;
+    
+    @javax.inject.Inject
+    private InteractionContext interactionContext;
+
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionServiceJdo.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionServiceJdo.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionServiceJdo.java
index d8d0420..5dd0387 100644
--- a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionServiceJdo.java
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/interaction/InteractionServiceJdo.java
@@ -16,34 +16,32 @@
  */
 package org.apache.isis.objectstore.jdo.applib.service.interaction;
 
-import java.util.List;
 import java.util.UUID;
 
-import javax.inject.Inject;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.isis.applib.AbstractService;
-import org.apache.isis.applib.annotation.Bookmarkable;
-import org.apache.isis.applib.annotation.Named;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.clock.Clock;
-import org.apache.isis.applib.query.QueryDefault;
 import org.apache.isis.applib.services.interaction.Interaction;
-import org.apache.isis.applib.services.interaction.InteractionContext;
 import org.apache.isis.applib.services.interaction.spi.InteractionFactory;
 
-@Named("Interactions")
-public class InteractionServiceJdo extends AbstractService implements InteractionFactory, InteractionRepository {
+public class InteractionServiceJdo extends AbstractService implements InteractionFactory {
 
     @SuppressWarnings("unused")
     private static final Logger LOG = LoggerFactory.getLogger(InteractionServiceJdo.class);
 
+    /**
+     * Creates an {@link InteractionJdo}, initializing its 
+     * {@link Interaction#setNature(Interaction.Nature) nature} to be
+     * {@link Interaction.Nature#RENDERING rendering}.
+     */
     @Programmatic
     @Override
     public Interaction create() {
         InteractionJdo interaction = newTransientInstance(InteractionJdo.class);
+        interaction.setNature(Interaction.Nature.RENDERING);
         return interaction;
     }
 
@@ -52,66 +50,37 @@ public class InteractionServiceJdo extends AbstractService implements Interactio
     public void startTransaction(Interaction interaction, UUID transactionId) {
         if(interaction instanceof InteractionJdo) {
             // should be the case, since this service created the object in the #create() method
-            final InteractionJdo interactionJdo = (InteractionJdo) interaction;
-            interactionJdo.setTransactionId(transactionId.toString());
+            InteractionJdo interactionJdo = (InteractionJdo) interaction;
+            if(interactionJdo.getTransactionId() == null) {
+                interactionJdo.setTransactionId(transactionId);
+            }
         }
     }
 
     @Programmatic
     @Override
     public void complete(final Interaction interaction) {
-        if(interaction.getActionIdentifier() == null) {
-            // discard, no action occurred
+        InteractionJdo interactionJdo = asPersistableInteractionJdo(interaction);
+        if(interactionJdo == null) {
             return;
         }
-        if(interaction instanceof InteractionJdo) {
-            // should be the case, since this service created the object in the #create() method
-            final InteractionJdo interactionJdo = (InteractionJdo) interaction;
             
-            interactionJdo.setCompletedAt(Clock.getTimeAsJavaSqlTimestamp());
-            persistIfNotAlready(interaction);
-        }
-    }
-
-    @Override
-    public InteractionJdo findByTransactionId(final UUID transactionId) {
-        persistCurrentInteractionIfRequired();
-        return firstMatch(
-                new QueryDefault<InteractionJdo>(InteractionJdo.class, 
-                        "findByTransactionId", 
-                        "transactionId", transactionId.toString()));
+        interactionJdo.setCompletedAt(Clock.getTimeAsJavaSqlTimestamp());
+        
+        persistIfNotAlready(interactionJdo);
     }
 
-    private void persistCurrentInteractionIfRequired() {
-        if(interactionContext != null) {
-            // expect to be the case, given that this service has been configured
-            Interaction interaction = interactionContext.getInteraction();
+    /**
+     * Not API, factored out from {@link InteractionRepository}.
+     */
+    InteractionJdo asPersistableInteractionJdo(final Interaction interaction) {
+        if(interaction.getNature() == Interaction.Nature.ACTION_INVOCATION) {
             if(interaction instanceof InteractionJdo) {
-                // should be the case, since this service created the object in the #start() method
-                persistIfNotAlready(interaction);
+                // should be the case, since this service created the object in the #create() method
+                return (InteractionJdo)interaction;
             }
         }
+        // else, don't care
+        return null;
     }
-
-    @Bookmarkable
-    @Override
-    public List<InteractionJdo> currentInteractions() {
-        persistCurrentInteractionIfRequired();
-        return allMatches(
-                new QueryDefault<InteractionJdo>(InteractionJdo.class, 
-                        "findCurrent"));
-    }
-    
-    @Bookmarkable
-    @Override
-    public List<InteractionJdo> completedInteractions() {
-        persistCurrentInteractionIfRequired();
-        return allMatches(
-                new QueryDefault<InteractionJdo>(InteractionJdo.class, 
-                        "findCompleted"));
-    }
-    
-    @Inject
-    private InteractionContext interactionContext;
-
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEvent.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEvent.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEvent.java
index ef4bf16..f167bcb 100644
--- a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEvent.java
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEvent.java
@@ -20,6 +20,8 @@
 package org.apache.isis.objectstore.jdo.applib.service.publish;
 
 
+import java.util.UUID;
+
 import javax.jdo.annotations.IdentityType;
 
 import org.apache.isis.applib.DomainObjectContainer;
@@ -33,6 +35,7 @@ import org.apache.isis.applib.annotation.MultiLine;
 import org.apache.isis.applib.annotation.NotPersisted;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.annotation.TypicalLength;
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.services.HasTransactionId;
 import org.apache.isis.applib.services.publish.EventMetadata;
@@ -43,8 +46,16 @@ import org.apache.isis.applib.services.publish.EventType;
         table="IsisPublishedEvent")
 @javax.jdo.annotations.Queries( {
     @javax.jdo.annotations.Query(
-            name="publishedevent_of_state", language="JDOQL",  
-            value="SELECT FROM org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEvent WHERE state == :state ORDER BY timestamp")
+            name="findByStateOrderByTimestamp", language="JDOQL",  
+            value="SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEvent "
+                    + "WHERE state == :state ORDER BY timestamp"),
+    @javax.jdo.annotations.Query(
+            name="findByTransactionId", language="JDOQL",  
+            value="SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEvent "
+                    + "WHERE transactionId == :transactionId")
+            
 })
 @Immutable
 public class PublishedEvent implements HasTransactionId {
@@ -97,6 +108,7 @@ public class PublishedEvent implements HasTransactionId {
      */
     @javax.jdo.annotations.Column(length=47)
     @javax.jdo.annotations.PrimaryKey
+    @TypicalLength(47)
     @MemberOrder(sequence = "2")
     public String getId() {
         return id;
@@ -108,7 +120,7 @@ public class PublishedEvent implements HasTransactionId {
     
     // //////////////////////////////////////
 
-    private String transactionId;
+    private UUID transactionId;
 
     /**
      * Hidden (<tt>@Programmatic</tt>) because information also available in the {@link #getId() id}.
@@ -116,12 +128,12 @@ public class PublishedEvent implements HasTransactionId {
     @javax.jdo.annotations.Column(length=36)
     @Programmatic
     @Override
-    public String getTransactionId() {
+    public UUID getTransactionId() {
         return transactionId;
     }
 
     @Override
-    public void setTransactionId(final String transactionId) {
+    public void setTransactionId(final UUID transactionId) {
         this.transactionId = transactionId;
     }
     

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventContributions.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventContributions.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventContributions.java
new file mode 100644
index 0000000..f81d0f8
--- /dev/null
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventContributions.java
@@ -0,0 +1,47 @@
+/**
+ *  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.objectstore.jdo.applib.service.publish;
+
+import java.util.List;
+
+import org.apache.isis.applib.AbstractFactoryAndRepository;
+import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.Render;
+import org.apache.isis.applib.annotation.ActionSemantics.Of;
+import org.apache.isis.applib.annotation.NotContributed;
+import org.apache.isis.applib.annotation.NotContributed.As;
+import org.apache.isis.applib.annotation.Render.Type;
+import org.apache.isis.applib.annotation.NotInServiceMenu;
+import org.apache.isis.applib.services.HasTransactionId;
+
+
+public class PublishedEventContributions extends AbstractFactoryAndRepository {
+
+    @ActionSemantics(Of.SAFE)
+    @NotInServiceMenu
+    @NotContributed(As.ACTION)
+    @Render(Type.EAGERLY)
+    public List<PublishedEvent> publishedEvents(final HasTransactionId hasTransactionId) {
+        return publishedEventRepository.findByTransactionId(hasTransactionId.getTransactionId());
+    }
+    
+    // //////////////////////////////////////
+
+    @javax.inject.Inject
+    private PublishedEventRepository publishedEventRepository;
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventRepository.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventRepository.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventRepository.java
new file mode 100644
index 0000000..ebb170b
--- /dev/null
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventRepository.java
@@ -0,0 +1,71 @@
+/*
+ *  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.objectstore.jdo.applib.service.publish;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.isis.applib.AbstractFactoryAndRepository;
+import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.NotContributed;
+import org.apache.isis.applib.annotation.NotInServiceMenu;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.annotation.ActionSemantics.Of;
+import org.apache.isis.applib.annotation.NotContributed.As;
+import org.apache.isis.applib.query.QueryDefault;
+import org.apache.isis.applib.services.HasTransactionId;
+
+public class PublishedEventRepository extends AbstractFactoryAndRepository {
+
+    @Programmatic
+    public List<PublishedEvent> findQueued() {
+        return allMatches(
+                new QueryDefault<PublishedEvent>(PublishedEvent.class, 
+                        "findByStateOrderByTimestamp", 
+                        "state", PublishedEvent.State.QUEUED));
+    }
+
+    @Programmatic
+    public List<PublishedEvent> findProcessed() {
+        return allMatches(
+                new QueryDefault<PublishedEvent>(PublishedEvent.class, 
+                        "findByStateOrderByTimestamp", 
+                        "state", PublishedEvent.State.PROCESSED));
+    }
+
+    @Programmatic
+    public List<PublishedEvent> findByTransactionId(final UUID transactionId) {
+        return allMatches(
+                new QueryDefault<PublishedEvent>(PublishedEvent.class, 
+                        "findByTransactionId", 
+                        "transactionId", transactionId));
+    }
+
+    @Programmatic
+    public void purgeProcessed() {
+        // REVIEW: this is not particularly performant.
+        // much better would be to go direct to the JDO API.
+        List<PublishedEvent> processedEvents = findProcessed();
+        for (PublishedEvent publishedEvent : processedEvents) {
+            publishedEvent.delete();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdo.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdo.java b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdo.java
index 632aac6..b1fbc9d 100644
--- a/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdo.java
+++ b/component/objectstore/jdo/jdo-applib/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdo.java
@@ -19,15 +19,8 @@
 
 package org.apache.isis.objectstore.jdo.applib.service.publish;
 
-import java.util.List;
-
 import org.apache.isis.applib.AbstractService;
-import org.apache.isis.applib.annotation.ActionSemantics;
-import org.apache.isis.applib.annotation.ActionSemantics.Of;
-import org.apache.isis.applib.annotation.Hidden;
-import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.Named;
-import org.apache.isis.applib.query.QueryDefault;
+import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.services.publish.EventMetadata;
 import org.apache.isis.applib.services.publish.EventPayload;
 import org.apache.isis.applib.services.publish.EventSerializer;
@@ -37,19 +30,17 @@ import org.apache.isis.applib.services.publish.PublishingService;
  * An implementation of {@link PublishingService} that persists events as
  * entities into a JDO-backed database.
  */
-@Named("Integration")
 public class PublishingServiceJdo extends AbstractService implements PublishingService {
 
-    private EventSerializer eventSerializer;
 
     @Override
-    @Hidden
+    @Programmatic
     public void publish(EventMetadata metadata, EventPayload payload) {
         final String serializedEvent = eventSerializer.serialize(metadata, payload).toString();
         final PublishedEvent publishedEvent = newTransientInstance(PublishedEvent.class);
         publishedEvent.setSerializedForm(serializedEvent);
         publishedEvent.setId(metadata.getId());
-        publishedEvent.setTransactionId(metadata.getTransactionId().toString());
+        publishedEvent.setTransactionId(metadata.getTransactionId());
         publishedEvent.setSequence(metadata.getSequence());
         publishedEvent.setEventType(metadata.getEventType());
         publishedEvent.setTimestamp(metadata.getTimestamp());
@@ -58,36 +49,9 @@ public class PublishingServiceJdo extends AbstractService implements PublishingS
         persist(publishedEvent);
     }
 
-    @ActionSemantics(Of.SAFE)
-    @MemberOrder(sequence="1")
-    public List<PublishedEvent> queuedEvents() {
-        return allMatches(
-                new QueryDefault<PublishedEvent>(PublishedEvent.class, 
-                        "publishedevent_of_state", 
-                        "state", PublishedEvent.State.QUEUED));
-    }
-
-    @ActionSemantics(Of.SAFE)
-    @MemberOrder(sequence="2")
-    public List<PublishedEvent> processedEvents() {
-        return allMatches(
-                new QueryDefault<PublishedEvent>(PublishedEvent.class, 
-                        "publishedevent_of_state", 
-                        "state", PublishedEvent.State.PROCESSED));
-    }
-
-    @ActionSemantics(Of.SAFE)
-    @MemberOrder(sequence="3")
-    public void purgeProcessed() {
-        // REVIEW: this is not particularly performant.
-        // much better would be to go direct to the JDO API.
-        List<PublishedEvent> processedEvents = processedEvents();
-        for (PublishedEvent publishedEvent : processedEvents) {
-            publishedEvent.delete();
-        }
-    }
+    // //////////////////////////////////////
 
-    @Hidden
+    private EventSerializer eventSerializer;
     @Override
     public void setEventSerializer(EventSerializer eventSerializer) {
         this.eventSerializer = eventSerializer;

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/persistence/spi/JdoObjectIdSerializer.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/persistence/spi/JdoObjectIdSerializer.java b/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/persistence/spi/JdoObjectIdSerializer.java
index 932d90e..b7374f2 100644
--- a/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/persistence/spi/JdoObjectIdSerializer.java
+++ b/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/persistence/spi/JdoObjectIdSerializer.java
@@ -23,11 +23,13 @@ import java.lang.reflect.InvocationTargetException;
 import java.math.BigInteger;
 import java.util.Arrays;
 import java.util.List;
+import java.util.UUID;
 
 import javax.jdo.annotations.IdentityType;
 import javax.jdo.identity.ByteIdentity;
 import javax.jdo.identity.IntIdentity;
 import javax.jdo.identity.LongIdentity;
+import javax.jdo.identity.ObjectIdentity;
 import javax.jdo.identity.StringIdentity;
 
 import org.datanucleus.identity.OID;
@@ -63,6 +65,15 @@ public final class JdoObjectIdSerializer {
         if(jdoOid instanceof javax.jdo.identity.LongIdentity) {
             return "l" + SEPARATOR + jdoOid; 
         }
+        if(jdoOid instanceof javax.jdo.identity.ObjectIdentity) {
+            javax.jdo.identity.ObjectIdentity id = (ObjectIdentity) jdoOid;
+            Object keyAsObject = id.getKeyAsObject();
+            // UUID support
+            if(keyAsObject instanceof UUID) {
+                UUID uuid = (UUID) keyAsObject;
+                return "u" + SEPARATOR + uuid.toString(); 
+            }
+        }
         if(jdoOid instanceof OID) {
             OID dnOid = (OID) jdoOid;
             Object keyValue = dnOid.getKeyValue();
@@ -96,51 +107,57 @@ public final class JdoObjectIdSerializer {
     public static Object toJdoObjectId(RootOid oid) {
 
         String idStr = oid.getIdentifier();
-        final int colonIdx = idStr.indexOf(SEPARATOR);
-        final String keyStr = idStr.substring(colonIdx+1);
+        final int separatorIdx = idStr.indexOf(SEPARATOR);
         
-        final String firstPart = idStr.substring(0, colonIdx);
+        final String distinguisher = idStr.substring(0, separatorIdx);
+        final String keyStr = idStr.substring(separatorIdx+1);
 
         final ObjectSpecification spec = getSpecificationLoader().lookupBySpecId(oid.getObjectSpecId());
-        JdoPersistenceCapableFacet facet = spec.getFacet(JdoPersistenceCapableFacet.class);
-        if(facet != null && facet.getIdentityType() == IdentityType.APPLICATION) {
+        final JdoPersistenceCapableFacet jdoPcFacet = spec.getFacet(JdoPersistenceCapableFacet.class);
 
-            if("s".equals(firstPart)) {
+        if(isApplicationIdentity(jdoPcFacet)) {
+
+            if("s".equals(distinguisher)) {
                 return keyStr;
             }
-            if("i".equals(firstPart)) {
+            if("i".equals(distinguisher)) {
                 return Integer.parseInt(keyStr);
             }
-            if("l".equals(firstPart)) {
+            if("l".equals(distinguisher)) {
                 return Long.parseLong(keyStr);
             }
-            if("b".equals(firstPart)) {
+            if("b".equals(distinguisher)) {
                 return Byte.parseByte(keyStr);
             }
+            if("u".equals(distinguisher)) {
+                return UUID.fromString(keyStr);
+            }
             
         } else {
 
-            if("s".equals(firstPart)) {
+            if("s".equals(distinguisher)) {
                 return new StringIdentity(objectTypeClassFor(oid), keyStr);
             }
-            if("i".equals(firstPart)) {
+            if("i".equals(distinguisher)) {
                 return new IntIdentity(objectTypeClassFor(oid), keyStr);
             }
-            if("l".equals(firstPart)) {
+            if("l".equals(distinguisher)) {
                 return new LongIdentity(objectTypeClassFor(oid), keyStr);
             }
-            if("b".equals(firstPart)) {
+            if("b".equals(distinguisher)) {
                 return new ByteIdentity(objectTypeClassFor(oid), keyStr);
             }
-
+            if("u".equals(distinguisher)) {
+                return new ObjectIdentity(objectTypeClassFor(oid), UUID.fromString(keyStr));
+            }
         }
         
 
-        if(dnPrefixes.contains(firstPart)) {
+        if(dnPrefixes.contains(distinguisher)) {
 			return keyStr + "[OID]" + spec.getFullIdentifier(); 
         }
         
-        final String clsName = firstPart;
+        final String clsName = distinguisher;
         try {
             final Class<?> cls = Thread.currentThread().getContextClassLoader().loadClass(clsName);
             final Constructor<?> cons = cls.getConstructor(String.class);
@@ -163,6 +180,10 @@ public final class JdoObjectIdSerializer {
         }
     }
 
+    protected static boolean isApplicationIdentity(final JdoPersistenceCapableFacet jdoPcFacet) {
+        return jdoPcFacet != null && jdoPcFacet.getIdentityType() == IdentityType.APPLICATION;
+    }
+
 	private static Class<?> objectTypeClassFor(RootOid oid) {
 		final ObjectSpecId objectSpecId = oid.getObjectSpecId();
 		final ObjectSpecification spec = getSpecificationLoader().lookupBySpecId(objectSpecId);

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java
index de310f0..1ba602e 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java
@@ -21,17 +21,26 @@ package org.apache.isis.viewer.wicket.ui.components.actions;
 
 import java.util.List;
 
+import com.google.common.base.Throwables;
+
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.model.Model;
 
 import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
 import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer;
 import org.apache.isis.applib.services.exceprecog.ExceptionRecognizerComposite;
+import org.apache.isis.applib.services.interaction.Interaction;
+import org.apache.isis.applib.services.interaction.InteractionContext;
 import org.apache.isis.core.commons.authentication.MessageBroker;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.oid.Oid;
+import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
+import org.apache.isis.core.metamodel.consent.InteractionContextType;
 import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager;
@@ -163,6 +172,7 @@ public class ActionPanel extends PanelAbstract<ActionModel> implements ActionExe
             final AjaxRequestTarget target, 
             final Form<?> feedbackForm) {
 
+        
         final ActionModel actionModel = getActionModel();
         
         // validate the action parameters (if any)
@@ -171,7 +181,18 @@ public class ActionPanel extends PanelAbstract<ActionModel> implements ActionExe
         if (invalidReasonIfAny != null) {
             raiseWarning(target, feedbackForm, invalidReasonIfAny);
             return false;
-        } 
+        }
+        
+        final InteractionContext interactionContext = getServicesInjector().lookupService(InteractionContext.class);
+        final Interaction interaction;
+        if (interactionContext != null) {
+            interaction = interactionContext.getInteraction();
+            interaction.setNature(Interaction.Nature.ACTION_INVOCATION);
+        } else {
+            interaction = null;
+        }
+        
+        
         // the object store could raise an exception (eg uniqueness constraint)
         // so we handle it here.
         try {
@@ -183,8 +204,9 @@ public class ActionPanel extends PanelAbstract<ActionModel> implements ActionExe
             getTransactionManager().flushTransaction();
             
             ActionResultResponse resultResponse = ActionResultResponseType.determineAndInterpretResult(this.getActionModel(), resultAdapter);
-            final ActionResultResponseHandlingStrategy resultType = ActionResultResponseHandlingStrategy.determineFor(resultResponse);
-            resultType.handleResults(this, resultResponse);
+            final ActionResultResponseHandlingStrategy responseHandlingStrategy = 
+                    ActionResultResponseHandlingStrategy.determineFor(resultResponse);
+            responseHandlingStrategy.handleResults(this, resultResponse);
 
             if (actionModel.isBookmarkable()) {
                 bookmarkPage(actionModel);
@@ -210,11 +232,16 @@ public class ActionPanel extends PanelAbstract<ActionModel> implements ActionExe
                 return false;
             }
             
-            // not handled, so propagate
+            // not handled, so capture and propagate
+            if(interaction != null) {
+                interaction.setException(Throwables.getStackTraceAsString(ex));
+            }
+
             throw ex;
         }
     }
 
+
     private String recognizeException(RuntimeException ex, AjaxRequestTarget target, Form<?> feedbackForm) {
         
         // REVIEW: this code is similar to stuff in EntityPropertiesForm, perhaps move up to superclass?

http://git-wip-us.apache.org/repos/asf/isis/blob/b9964003/core/applib/src/main/java/org/apache/isis/applib/annotation/Audited.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/Audited.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/Audited.java
index 5749314..dbd82db 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/Audited.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/Audited.java
@@ -25,13 +25,13 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-import org.apache.isis.applib.services.audit.AuditingService;
+import org.apache.isis.applib.services.audit.AuditingService3;
 
 /**
  * Indicates that the entity should be audited.
  * 
  * <p>
- * Requires that an implementation of the {@link AuditingService} is registered with the framework.
+ * Requires that an implementation of the {@link AuditingService3} is registered with the framework.
  * 
  * <p>
  * Check that the configured object store supports the annotation.  For example, the