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/07/04 14:46:52 UTC

[10/12] ISIS-833: moved applib services into own modules, rationalized most of the applib/runtime modules

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventJdo.java
----------------------------------------------------------------------
diff --git a/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventJdo.java b/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventJdo.java
new file mode 100644
index 0000000..097f64c
--- /dev/null
+++ b/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventJdo.java
@@ -0,0 +1,490 @@
+/*
+ *  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.UUID;
+import javax.jdo.annotations.IdentityType;
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.applib.annotation.*;
+import org.apache.isis.applib.annotation.ActionSemantics.Of;
+import org.apache.isis.applib.services.HasTransactionId;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.services.publish.EventType;
+import org.apache.isis.applib.util.ObjectContracts;
+import org.apache.isis.applib.util.TitleBuffer;
+import org.apache.isis.objectstore.jdo.applib.service.DomainChangeJdoAbstract;
+import org.apache.isis.objectstore.jdo.applib.service.JdoColumnLength;
+import org.apache.isis.objectstore.jdo.applib.service.Util;
+
+@javax.jdo.annotations.PersistenceCapable(
+        identityType=IdentityType.APPLICATION,
+        table="IsisPublishedEvent", 
+        objectIdClass=PublishedEventJdoPK.class)
+@javax.jdo.annotations.Queries( {
+    @javax.jdo.annotations.Query(
+            name="findByStateOrderByTimestamp", language="JDOQL",  
+            value="SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEventJdo "
+                    + "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.PublishedEventJdo "
+                    + "WHERE transactionId == :transactionId"),
+    @javax.jdo.annotations.Query(
+            name="findByTargetAndTimestampBetween", language="JDOQL",  
+            value="SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEventJdo "
+                    + "WHERE targetStr == :targetStr " 
+                    + "&& timestamp >= :from " 
+                    + "&& timestamp <= :to "
+                    + "ORDER BY timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name="findByTargetAndTimestampAfter", language="JDOQL",  
+            value="SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEventJdo "
+                    + "WHERE targetStr == :targetStr " 
+                    + "&& timestamp >= :from "
+                    + "ORDER BY timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name="findByTargetAndTimestampBefore", language="JDOQL",  
+            value="SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEventJdo "
+                    + "WHERE targetStr == :targetStr " 
+                    + "&& timestamp <= :to "
+                    + "ORDER BY timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name="findByTarget", language="JDOQL",  
+            value="SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEventJdo "
+                    + "WHERE targetStr == :targetStr " 
+                    + "ORDER BY timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name="findByTimestampBetween", language="JDOQL",  
+            value="SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEventJdo "
+                    + "WHERE timestamp >= :from " 
+                    + "&&    timestamp <= :to "
+                    + "ORDER BY timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name="findByTimestampAfter", language="JDOQL",  
+            value="SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEventJdo "
+                    + "WHERE timestamp >= :from "
+                    + "ORDER BY timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name="findByTimestampBefore", language="JDOQL",  
+            value="SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEventJdo "
+                    + "WHERE timestamp <= :to "
+                    + "ORDER BY timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name="find", language="JDOQL",  
+            value="SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEventJdo "
+                    + "ORDER BY timestamp DESC")
+})
+@MemberGroupLayout(
+        columnSpans={6,0,6},
+        left={"Identifiers","Target"},
+        right={"Detail","State"})
+@Immutable
+@Named("Published Event")
+@ObjectType("IsisPublishedEvent")
+public class PublishedEventJdo extends DomainChangeJdoAbstract implements HasTransactionId {
+
+    public static enum State {
+        QUEUED, PROCESSED
+    }
+
+    // //////////////////////////////////////
+
+    public PublishedEventJdo() {
+        super(ChangeType.PUBLISHED_EVENT);
+    }
+
+
+    // //////////////////////////////////////
+    // Identification
+    // //////////////////////////////////////
+
+    public String title() {
+        final TitleBuffer buf = new TitleBuffer();
+        buf.append(getEventType().name()).append(" ").append(getTargetStr());
+        if(getEventType()==EventType.ACTION_INVOCATION) {
+            buf.append(" ").append(getMemberIdentifier());
+        }
+        buf.append(",").append(getState());
+        return buf.toString();
+    }
+
+
+    // //////////////////////////////////////
+    // user (property)
+    // //////////////////////////////////////
+
+    private String user;
+    
+    @javax.jdo.annotations.Column(allowsNull="false", length=50)
+    @MemberOrder(name="Identifiers", sequence = "10")
+    @Hidden(where=Where.PARENTED_TABLES)
+    public String getUser() {
+        return user;
+    }
+    
+    public void setUser(final String user) {
+        this.user = user;
+    }
+    
+
+    // //////////////////////////////////////
+    // timestamp (property)
+    // //////////////////////////////////////
+
+    private java.sql.Timestamp timestamp;
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="false")
+    @MemberOrder(name="Identifiers", sequence = "20")
+    @Hidden(where=Where.PARENTED_TABLES)
+    public java.sql.Timestamp getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(final java.sql.Timestamp timestamp) {
+        this.timestamp = timestamp;
+    }
+    
+
+
+    // //////////////////////////////////////
+    // transactionId
+    // //////////////////////////////////////
+
+    private UUID transactionId;
+
+    /**
+     * The unique identifier (a GUID) of the transaction in which this published event was persisted.
+     * 
+     * <p>
+     * The combination of ({@link #getTransactionId() transactionId}, {@link #getSequence() sequence}) makes up the
+     * primary key.
+     */
+    @javax.jdo.annotations.PrimaryKey
+    @javax.jdo.annotations.Column(allowsNull="false",length=JdoColumnLength.TRANSACTION_ID)
+    @MemberOrder(name="Identifiers", sequence = "30")
+    @Hidden(where=Where.PARENTED_TABLES)
+    @Override
+    public UUID getTransactionId() {
+        return transactionId;
+    }
+
+    @Override
+    public void setTransactionId(final UUID transactionId) {
+        this.transactionId = transactionId;
+    }
+
+    
+    // //////////////////////////////////////
+    // sequence
+    // //////////////////////////////////////
+
+    private int sequence;
+
+    /**
+     * The 0-based additional identifier of a published event within the given {@link #getTransactionId() transaction}.
+     * 
+     * <p>
+     * The combination of ({@link #getTransactionId() transactionId}, {@link #getSequence() sequence}) makes up the
+     * primary key.
+     */
+    @javax.jdo.annotations.PrimaryKey
+    @MemberOrder(name="Identifiers", sequence = "40")
+    public int getSequence() {
+        return sequence;
+    }
+
+    public void setSequence(final int sequence) {
+        this.sequence = sequence;
+    }
+    
+
+    // //////////////////////////////////////
+    // title
+    // //////////////////////////////////////
+
+    private String title;
+
+    /**
+     * Consists of the full oidStr (with version info etc), concatenated 
+     * (if an {@link EventType#ACTION_INVOCATION}) with the name/parms of the action.
+     * 
+     * <p>
+     * @deprecated - the oid of the target is also available (without the version info) through {@link #getTarget()}, and
+     *               the action identifier is available through {@link #getMemberIdentifier()}.
+     */
+    @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.PublishedEvent.TITLE)
+    @Hidden
+    @Deprecated
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(final String title) {
+        this.title = title;
+    }
+    
+    
+    // //////////////////////////////////////
+    // eventType (property)
+    // //////////////////////////////////////
+
+    private EventType eventType;
+
+    @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.PublishedEvent.EVENT_TYPE)
+    @MemberOrder(name="Identifiers",sequence = "50")
+    public EventType getEventType() {
+        return eventType;
+    }
+
+    public void setEventType(final EventType eventType) {
+        this.eventType = eventType;
+    }
+    
+
+    // //////////////////////////////////////
+    // targetClass (property)
+    // //////////////////////////////////////
+
+    private String targetClass;
+
+    @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.TARGET_CLASS)
+    @TypicalLength(30)
+    @MemberOrder(name="Target", sequence = "10")
+    @Named("Class")
+    public String getTargetClass() {
+        return targetClass;
+    }
+
+    public void setTargetClass(final String targetClass) {
+        this.targetClass = Util.abbreviated(targetClass, JdoColumnLength.TARGET_CLASS);
+    }
+
+
+    // //////////////////////////////////////
+    // targetAction (property)
+    // //////////////////////////////////////
+    
+    private String targetAction;
+    
+    /**
+     * Only populated for {@link EventType#ACTION_INVOCATION}
+     */
+    @javax.jdo.annotations.Column(allowsNull="true", length=JdoColumnLength.TARGET_ACTION)
+    @TypicalLength(30)
+    @MemberOrder(name="Target", sequence = "20")
+    @Named("Action")
+    public String getTargetAction() {
+        return targetAction;
+    }
+    
+    public void setTargetAction(final String targetAction) {
+        this.targetAction = Util.abbreviated(targetAction, JdoColumnLength.TARGET_ACTION);
+    }
+    
+
+    // //////////////////////////////////////
+    // target (property)
+    // openTargetObject (action)
+    // //////////////////////////////////////
+
+    
+    private String targetStr;
+    @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.BOOKMARK, name="target")
+    @MemberOrder(name="Target", sequence="30")
+    @Named("Object")
+    public String getTargetStr() {
+        return targetStr;
+    }
+
+    public void setTargetStr(final String targetStr) {
+        this.targetStr = targetStr;
+    }
+
+
+    // //////////////////////////////////////
+    // memberIdentifier (property)
+    // //////////////////////////////////////
+
+    private String memberIdentifier;
+    
+    /**
+     * Holds a string representation of the invoked action, equivalent to
+     * {@link Identifier#toClassAndNameIdentityString()}.
+     * 
+     * <p>
+     * Only populated for {@link EventType#ACTION_INVOCATION}, 
+     * returns <tt>null</tt> otherwise.
+     * 
+     * <p>
+     * This property is called 'memberIdentifier' rather than 'actionIdentifier' for
+     * consistency with other services (such as auditing and publishing) that may act on
+     * properties rather than simply just actions.
+     */
+    @javax.jdo.annotations.Column(allowsNull="true", length=JdoColumnLength.MEMBER_IDENTIFIER)
+    @TypicalLength(60)
+    @Hidden(where=Where.ALL_TABLES)
+    @MemberOrder(name="Detail",sequence = "20")
+    public String getMemberIdentifier() {
+        return memberIdentifier;
+    }
+
+    public void setMemberIdentifier(final String actionIdentifier) {
+        this.memberIdentifier = Util.abbreviated(actionIdentifier, JdoColumnLength.MEMBER_IDENTIFIER);
+    }
+
+
+
+    // //////////////////////////////////////
+    // state (property)
+    // //////////////////////////////////////
+
+    private State state;
+
+    @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.PublishedEvent.STATE)
+    @MemberOrder(name="State", sequence = "30")
+    public State getState() {
+        return state;
+    }
+
+    public void setState(final State state) {
+        this.state = state;
+    }
+    private PublishedEventJdo setStateAndReturn(State state) {
+        setState(state);
+        return this;
+    }
+    
+
+    // //////////////////////////////////////
+    // serializedFormZipped (property)
+    // serializedForm (derived property)
+    // //////////////////////////////////////
+
+    @javax.jdo.annotations.NotPersistent
+    @NotPersisted
+    @MultiLine(numberOfLines=14)
+    @Hidden(where=Where.ALL_TABLES)
+    @MemberOrder(name="Detail", sequence = "40")
+    public String getSerializedForm() {
+        byte[] zipped = getSerializedFormZipped();
+        if(zipped != null) {
+            return PublishingServiceJdo.fromZippedBytes(zipped);
+        } else {
+            return getSerializedFormClob();
+        }
+    }
+
+
+    // //////////////////////////////////////
+
+    @Deprecated
+    @javax.jdo.annotations.Column(allowsNull="true")
+    private byte[] serializedFormZipped;
+
+    @Deprecated
+    @Programmatic // ignored by Isis
+    public byte[] getSerializedFormZipped() {
+        return serializedFormZipped;
+    }
+
+    @Deprecated
+    public void setSerializedFormZipped(final byte[] serializedFormZipped) {
+        this.serializedFormZipped = serializedFormZipped;
+    }
+
+    // //////////////////////////////////////
+
+    private String serializedFormClob;
+
+    @Programmatic // ignored by Isis
+    @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB")
+    public String getSerializedFormClob() {
+        return serializedFormClob;
+    }
+
+    public void setSerializedFormClob(final String serializedFormClob) {
+        this.serializedFormClob = serializedFormClob;
+    }
+
+
+    // //////////////////////////////////////
+    // processed (action)
+    // reQueue   (action)
+    // delete    (action)
+    // //////////////////////////////////////
+
+ 
+    @Bulk
+    @ActionSemantics(Of.IDEMPOTENT)
+    @MemberOrder( name="State", sequence="10")
+    public PublishedEventJdo processed() {
+        return setStateAndReturn(State.PROCESSED);
+    }
+
+
+    @Bulk
+    @ActionSemantics(Of.IDEMPOTENT)
+    @MemberOrder(name="State", sequence="11")
+    public PublishedEventJdo reQueue() {
+        return setStateAndReturn(State.QUEUED);
+    }
+
+    @Bulk
+    @MemberOrder(name="State", sequence="12")
+    public void delete() {
+        container.removeIfNotAlready(this);
+    }
+    
+
+    
+    // //////////////////////////////////////
+    // toString
+    // //////////////////////////////////////
+
+    @Override
+    public String toString() {
+        return ObjectContracts.toString(this, "targetStr,timestamp,user,eventType,memberIdentifier,state");
+    }
+
+
+    // //////////////////////////////////////
+    // dependencies
+    // //////////////////////////////////////
+
+    @javax.inject.Inject
+    private BookmarkService bookmarkService;
+
+    @javax.inject.Inject
+    private DomainObjectContainer container;
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventJdoPK.java
----------------------------------------------------------------------
diff --git a/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventJdoPK.java b/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventJdoPK.java
new file mode 100644
index 0000000..0666ed3
--- /dev/null
+++ b/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishedEventJdoPK.java
@@ -0,0 +1,102 @@
+/*
+ *  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.io.Serializable;
+import java.util.StringTokenizer;
+import java.util.UUID;
+
+public class PublishedEventJdoPK implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String SEPARATOR = "_";
+
+    public UUID transactionId;
+    public int sequence;
+
+    // //////////////////////////////////////
+
+    
+    public PublishedEventJdoPK() {
+    }
+    
+    public PublishedEventJdoPK(final String value) {
+        final StringTokenizer token = new StringTokenizer (value, SEPARATOR);
+        this.transactionId = UUID.fromString(token.nextToken());
+        this.sequence = Integer.parseInt(token.nextToken());
+    }
+
+    // //////////////////////////////////////
+
+    public UUID getTransactionId() {
+        return transactionId;
+    }
+    public void setTransactionId(UUID transactionId) {
+        this.transactionId = transactionId;
+    }
+    
+    // //////////////////////////////////////
+
+    public int getSequence() {
+        return sequence;
+    }
+    public void setSequence(int sequence) {
+        this.sequence = sequence;
+    }
+    
+    // //////////////////////////////////////
+
+    
+    
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + sequence;
+        result = prime * result + ((transactionId == null) ? 0 : transactionId.hashCode());
+        return result;
+    }
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        PublishedEventJdoPK other = (PublishedEventJdoPK) obj;
+        if (sequence != other.sequence)
+            return false;
+        if (transactionId == null) {
+            if (other.transactionId != null)
+                return false;
+        } else if (!transactionId.equals(other.transactionId))
+            return false;
+        return true;
+    }
+    
+    // //////////////////////////////////////
+
+    
+    @Override
+    public String toString() {
+        return transactionId + SEPARATOR + sequence;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdo.java
----------------------------------------------------------------------
diff --git a/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdo.java b/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdo.java
new file mode 100644
index 0000000..52903c8
--- /dev/null
+++ b/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdo.java
@@ -0,0 +1,122 @@
+/*
+ *  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.Map;
+import javax.annotation.PostConstruct;
+import org.apache.isis.applib.AbstractService;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.services.command.CommandContext;
+import org.apache.isis.applib.services.publish.EventMetadata;
+import org.apache.isis.applib.services.publish.EventPayload;
+import org.apache.isis.applib.services.publish.EventSerializer;
+import org.apache.isis.applib.services.publish.PublishingService;
+
+/**
+ * An implementation of {@link PublishingService} that persists events as
+ * entities into a JDO-backed database.
+ */
+public class PublishingServiceJdo extends AbstractService implements PublishingService {
+
+    private static final String SERIALIZED_FORM_LOCAL_KEY = "datanucleus.PublishingService.serializedForm";
+    private final static String SERIALIZED_FORM_KEY = "isis.persistor." + SERIALIZED_FORM_LOCAL_KEY;
+
+    static enum SerializedForm {
+        CLOB,
+        @Deprecated
+        ZIPPED;
+        static SerializedForm parse(final String value) {
+            return CLOB.toString().equalsIgnoreCase(value)? CLOB: ZIPPED;
+        }
+    }
+    
+    private SerializedForm serializedForm;
+
+    @Programmatic
+    @PostConstruct
+    public void init(Map<String,String> configuration) {
+        ensureDependenciesInjected();
+        serializedForm = SerializedForm.parse(configuration.get(SERIALIZED_FORM_KEY));
+    }
+
+    
+    // //////////////////////////////////////
+    
+    private void ensureDependenciesInjected() {
+        if(this.commandContext == null) {
+            throw new IllegalStateException(this.getClassName() + " requires CommandContext service to be configured");
+        }
+        if(this.eventSerializer == null) {
+            throw new IllegalStateException(this.getClassName() + " requires EventSerializer service to be configured");
+        }
+    }
+
+    
+    @Override
+    @Programmatic
+    public void publish(final EventMetadata metadata, final EventPayload payload) {
+        final String serializedEvent = eventSerializer.serialize(metadata, payload).toString();
+        final PublishedEventJdo publishedEvent = newTransientInstance(PublishedEventJdo.class);
+
+        if(this.serializedForm == SerializedForm.ZIPPED) {
+            final byte[] zippedBytes = asZippedBytes(serializedEvent);
+            publishedEvent.setSerializedFormZipped(zippedBytes);
+        } else {
+            publishedEvent.setSerializedFormClob(serializedEvent);
+        }
+        
+        publishedEvent.setTransactionId(metadata.getTransactionId());
+        publishedEvent.setSequence(metadata.getSequence());
+        publishedEvent.setEventType(metadata.getEventType());
+        publishedEvent.setTimestamp(metadata.getJavaSqlTimestamp());
+        publishedEvent.setUser(metadata.getUser());
+        publishedEvent.setTitle(metadata.getTitle());
+        
+        publishedEvent.setTargetClass(metadata.getTargetClass());
+        publishedEvent.setTarget(metadata.getTarget());
+        publishedEvent.setTargetAction(metadata.getTargetAction());
+        publishedEvent.setMemberIdentifier(metadata.getActionIdentifier());
+        
+        persist(publishedEvent);
+    }
+
+
+    static byte[] asZippedBytes(final String serializedEvent) {
+        return IoUtils.toUtf8ZippedBytes("serializedForm", serializedEvent);
+    }
+
+
+    // //////////////////////////////////////
+
+    private EventSerializer eventSerializer;
+    
+    @Override
+    public void setEventSerializer(EventSerializer eventSerializer) {
+        this.eventSerializer = eventSerializer;
+    }
+
+    static String fromZippedBytes(byte[] zipped) {
+        return IoUtils.fromUtf8ZippedBytes("serializedForm", zipped);
+    }
+
+
+    @javax.inject.Inject
+    private CommandContext commandContext;
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdoContributions.java
----------------------------------------------------------------------
diff --git a/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdoContributions.java b/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdoContributions.java
new file mode 100644
index 0000000..1709f64
--- /dev/null
+++ b/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdoContributions.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 PublishingServiceJdoContributions extends AbstractFactoryAndRepository {
+
+    @ActionSemantics(Of.SAFE)
+    @NotInServiceMenu
+    @NotContributed(As.ACTION)
+    @Render(Type.EAGERLY)
+    public List<PublishedEventJdo> publishedEvents(final HasTransactionId hasTransactionId) {
+        return publishedEventRepository.findByTransactionId(hasTransactionId.getTransactionId());
+    }
+    
+    // //////////////////////////////////////
+
+    @javax.inject.Inject
+    private PublishingServiceJdoRepository publishedEventRepository;
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdoRepository.java
----------------------------------------------------------------------
diff --git a/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdoRepository.java b/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdoRepository.java
new file mode 100644
index 0000000..636369d
--- /dev/null
+++ b/core/module-publishing-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/publish/PublishingServiceJdoRepository.java
@@ -0,0 +1,156 @@
+/*
+ *  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.sql.Timestamp;
+import java.util.List;
+import java.util.UUID;
+import org.joda.time.LocalDate;
+import org.apache.isis.applib.AbstractFactoryAndRepository;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.query.Query;
+import org.apache.isis.applib.query.QueryDefault;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+
+/**
+ * Provides supporting functionality for querying and persisting
+ * {@link org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEventJdo published event} entities.
+ *
+ * <p>
+ * This supporting service with no UI and no side-effects, and is there are no other implementations of the service,
+ * thus has been annotated with {@link org.apache.isis.applib.annotation.DomainService}.  This means that there is no
+ * need to explicitly register it as a service (eg in <tt>isis.properties</tt>).
+ */
+@DomainService
+public class PublishingServiceJdoRepository extends AbstractFactoryAndRepository {
+
+    @Programmatic
+    public List<PublishedEventJdo> findQueued() {
+        return allMatches(
+                new QueryDefault<PublishedEventJdo>(PublishedEventJdo.class, 
+                        "findByStateOrderByTimestamp", 
+                        "state", PublishedEventJdo.State.QUEUED));
+    }
+
+    @Programmatic
+    public List<PublishedEventJdo> findProcessed() {
+        return allMatches(
+                new QueryDefault<PublishedEventJdo>(PublishedEventJdo.class, 
+                        "findByStateOrderByTimestamp", 
+                        "state", PublishedEventJdo.State.PROCESSED));
+    }
+
+    @Programmatic
+    public List<PublishedEventJdo> findByTransactionId(final UUID transactionId) {
+        return allMatches(
+                new QueryDefault<PublishedEventJdo>(PublishedEventJdo.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<PublishedEventJdo> processedEvents = findProcessed();
+        for (PublishedEventJdo publishedEvent : processedEvents) {
+            publishedEvent.delete();
+        }
+    }
+
+
+    @Programmatic
+    public List<PublishedEventJdo> findByTargetAndFromAndTo(
+            final Bookmark target, 
+            final LocalDate from, 
+            final LocalDate to) {
+        final String targetStr = target.toString();
+        final Timestamp fromTs = toTimestampStartOfDayWithOffset(from, 0);
+        final Timestamp toTs = toTimestampStartOfDayWithOffset(to, 1);
+        
+        final Query<PublishedEventJdo> query;
+        if(from != null) {
+            if(to != null) {
+                query = new QueryDefault<PublishedEventJdo>(PublishedEventJdo.class, 
+                        "findByTargetAndTimestampBetween", 
+                        "targetStr", targetStr,
+                        "from", fromTs,
+                        "to", toTs);
+            } else {
+                query = new QueryDefault<PublishedEventJdo>(PublishedEventJdo.class, 
+                        "findByTargetAndTimestampAfter", 
+                        "targetStr", targetStr,
+                        "from", fromTs);
+            }
+        } else {
+            if(to != null) {
+                query = new QueryDefault<PublishedEventJdo>(PublishedEventJdo.class, 
+                        "findByTargetAndTimestampBefore", 
+                        "targetStr", targetStr,
+                        "to", toTs);
+            } else {
+                query = new QueryDefault<PublishedEventJdo>(PublishedEventJdo.class, 
+                        "findByTarget", 
+                        "targetStr", targetStr);
+            }
+        }
+        return allMatches(query);
+    }
+
+    @Programmatic
+    public List<PublishedEventJdo> findByFromAndTo(
+            final LocalDate from, 
+            final LocalDate to) {
+        final Timestamp fromTs = toTimestampStartOfDayWithOffset(from, 0);
+        final Timestamp toTs = toTimestampStartOfDayWithOffset(to, 1);
+        
+        final Query<PublishedEventJdo> query;
+        if(from != null) {
+            if(to != null) {
+                query = new QueryDefault<PublishedEventJdo>(PublishedEventJdo.class, 
+                        "findByTimestampBetween", 
+                        "from", fromTs,
+                        "to", toTs);
+            } else {
+                query = new QueryDefault<PublishedEventJdo>(PublishedEventJdo.class, 
+                        "findByTimestampAfter", 
+                        "from", fromTs);
+            }
+        } else {
+            if(to != null) {
+                query = new QueryDefault<PublishedEventJdo>(PublishedEventJdo.class, 
+                        "findByTimestampBefore", 
+                        "to", toTs);
+            } else {
+                query = new QueryDefault<PublishedEventJdo>(PublishedEventJdo.class, 
+                        "find");
+            }
+        }
+        return allMatches(query);
+    }
+    
+    private static Timestamp toTimestampStartOfDayWithOffset(final LocalDate dt, int daysOffset) {
+        return dt!=null
+                ?new java.sql.Timestamp(dt.toDateTimeAtStartOfDay().plusDays(daysOffset).getMillis())
+                :null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-publishing-jdo/src/main/resources/images/PublishedEventJdo.png
----------------------------------------------------------------------
diff --git a/core/module-publishing-jdo/src/main/resources/images/PublishedEventJdo.png b/core/module-publishing-jdo/src/main/resources/images/PublishedEventJdo.png
new file mode 100644
index 0000000..cb3a325
Binary files /dev/null and b/core/module-publishing-jdo/src/main/resources/images/PublishedEventJdo.png differ

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-publishing-jdo/src/test/java/org/apache/isis/objectstore/jdo/applib/service/publish/IoUtilsTest.java
----------------------------------------------------------------------
diff --git a/core/module-publishing-jdo/src/test/java/org/apache/isis/objectstore/jdo/applib/service/publish/IoUtilsTest.java b/core/module-publishing-jdo/src/test/java/org/apache/isis/objectstore/jdo/applib/service/publish/IoUtilsTest.java
new file mode 100644
index 0000000..32cfde0
--- /dev/null
+++ b/core/module-publishing-jdo/src/test/java/org/apache/isis/objectstore/jdo/applib/service/publish/IoUtilsTest.java
@@ -0,0 +1,36 @@
+/**
+ *  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 static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class IoUtilsTest {
+
+    @Test
+    public void roundtrip() {
+        final String str = "3784y5hrfbdgkjh3qyri f£$%$YTRGFDGER$£\"Eu098987u'!\"£%^&*IO(LUKJM)";
+        final byte[] utf8ZippedBytes = IoUtils.toUtf8ZippedBytes("serializedForm", str);
+        
+        final String str2 = IoUtils.fromUtf8ZippedBytes("serializedForm", utf8ZippedBytes);
+        
+        assertThat(str, (is(str2)));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-publishingeventserializer-ro/pom.xml
----------------------------------------------------------------------
diff --git a/core/module-publishingeventserializer-ro/pom.xml b/core/module-publishingeventserializer-ro/pom.xml
new file mode 100644
index 0000000..3feadb5
--- /dev/null
+++ b/core/module-publishingeventserializer-ro/pom.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.isis.core</groupId>
+		<artifactId>isis</artifactId>
+        <version>1.6.0-SNAPSHOT</version>
+	</parent>
+
+    <groupId>org.apache.isis.module</groupId>
+	<artifactId>isis-module-publishingeventserializer-ro</artifactId>
+
+	<name>Isis Module: Publishing Event Servializer (Restful Objects spec)</name>
+	<description>
+		Implementation of an event serializer (as required by the publishing 
+        service) that serialized the object as a JSON representation defined
+        by the Restful Objects spec.
+	</description>
+
+	<properties>
+        <siteBaseDir>..</siteBaseDir>
+		<relativeUrl>module-publishingeventserializer-ro/</relativeUrl>
+	</properties>
+
+    <url>http://isis.apache.org/${relativeUrl}</url>
+
+	<build>
+        <resources>
+            <resource>
+                <filtering>false</filtering>
+                <directory>src/main/resources</directory>
+            </resource>
+            <resource>
+                <filtering>false</filtering>
+                <directory>src/main/java</directory>
+                <includes>
+                    <include>**</include>
+                </includes>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </resource>
+        </resources>
+	</build>
+
+	<dependencies>
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-applib</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-viewer-restfulobjects-rendering</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-unittestsupport</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-publishingeventserializer-ro/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/eventserializer/EventSerializerRendererContext.java
----------------------------------------------------------------------
diff --git a/core/module-publishingeventserializer-ro/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/eventserializer/EventSerializerRendererContext.java b/core/module-publishingeventserializer-ro/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/eventserializer/EventSerializerRendererContext.java
new file mode 100644
index 0000000..6d10cc2
--- /dev/null
+++ b/core/module-publishingeventserializer-ro/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/eventserializer/EventSerializerRendererContext.java
@@ -0,0 +1,95 @@
+/*
+ *  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.viewer.restfulobjects.rendering.eventserializer;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import com.google.common.collect.Sets;
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.applib.profiles.Localization;
+import org.apache.isis.core.commons.authentication.AuthenticationSession;
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
+import org.apache.isis.core.metamodel.adapter.oid.Oid;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.runtime.system.persistence.PersistenceSession;
+import org.apache.isis.viewer.restfulobjects.rendering.RendererContext;
+
+class EventSerializerRendererContext implements RendererContext {
+
+    private final String baseUrl;
+    private final Where where;
+    
+    public EventSerializerRendererContext(String baseUrl, Where where) {
+        this.baseUrl = baseUrl;
+        this.where = where;
+    }
+
+    @Override
+    public String urlFor(String url) {
+        return baseUrl + url;
+    }
+
+    @Override
+    public AuthenticationSession getAuthenticationSession() {
+        return IsisContext.getAuthenticationSession();
+    }
+
+    @Override
+    public PersistenceSession getPersistenceSession() {
+        return IsisContext.getPersistenceSession();
+    }
+
+    @Override
+    public IsisConfiguration getConfiguration() {
+        return IsisContext.getConfiguration();
+    }
+
+    @Override
+    public AdapterManager getAdapterManager() {
+        return getPersistenceSession().getAdapterManager();
+    }
+
+    @Override
+    public List<List<String>> getFollowLinks() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public Where getWhere() {
+        return where;
+    }
+
+    @Override
+    public Localization getLocalization() {
+        return IsisContext.getLocalization();
+    }
+
+    private Set<Oid> rendered = Sets.newHashSet();
+    @Override
+    public boolean canEagerlyRender(ObjectAdapter objectAdapter) {
+        final Oid oid = objectAdapter.getOid();
+        return rendered.add(oid);
+    }
+
+    
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-publishingeventserializer-ro/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/eventserializer/RestfulObjectsSpecEventSerializer.java
----------------------------------------------------------------------
diff --git a/core/module-publishingeventserializer-ro/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/eventserializer/RestfulObjectsSpecEventSerializer.java b/core/module-publishingeventserializer-ro/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/eventserializer/RestfulObjectsSpecEventSerializer.java
new file mode 100644
index 0000000..a95a934
--- /dev/null
+++ b/core/module-publishingeventserializer-ro/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/eventserializer/RestfulObjectsSpecEventSerializer.java
@@ -0,0 +1,124 @@
+/*
+ *  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.viewer.restfulobjects.rendering.eventserializer;
+
+import java.io.IOException;
+import java.util.Map;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.applib.services.publish.EventMetadata;
+import org.apache.isis.applib.services.publish.EventPayload;
+import org.apache.isis.applib.services.publish.EventSerializer;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
+import org.apache.isis.viewer.restfulobjects.applib.util.JsonMapper;
+import org.apache.isis.viewer.restfulobjects.rendering.RendererContext;
+import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.DomainObjectReprRenderer;
+
+/**
+ * Serializes {@link org.apache.isis.applib.services.publish.EventMetadata event metadata} and corresponding
+ * {@link org.apache.isis.applib.services.publish.EventPayload payload} into a JSON format corresponding to the
+ * domain object representation specified by the Restful Objects spec.
+ *
+ * <p>
+ * This implementation has no UI, has no side-effects, and there are no other implementations of the service API, and
+ * so it annotated with {@link org.apache.isis.applib.annotation.DomainService}.  This class is implemented in the
+ * <tt>o.a.i.module:isis-module-publishingeventserializer-ro</tt> module.  If that module is included in the classpath,
+ * it this means that this service is automatically registered; no further configuration is required.
+ */
+@DomainService
+public class RestfulObjectsSpecEventSerializer implements EventSerializer {
+
+    private final static String BASE_URL_KEY = "isis.viewer.restfulobjects.RestfulObjectsSpecEventSerializer.baseUrl";
+    private static final String BASE_URL_DEFAULT = "http://localhost:8080/restful/";
+
+    //region > init, shutdown
+    private String baseUrl;
+
+    @PostConstruct
+    public void init(Map<String,String> props) {
+        final String baseUrlFromConfig = props.get(BASE_URL_KEY);
+        baseUrl = baseUrlFromConfig != null? baseUrlFromConfig: BASE_URL_DEFAULT;
+    }
+
+    @PreDestroy
+    public void shutdown() {
+    }
+    //endregion
+
+    //region > serialize (API)
+    @Programmatic
+    @Override
+    public Object serialize(EventMetadata metadata, EventPayload payload) {
+        final RendererContext rendererContext = new EventSerializerRendererContext(baseUrl, Where.OBJECT_FORMS);
+
+        final JsonRepresentation payloadRepr = asPayloadRepr(rendererContext, payload);
+        final JsonRepresentation eventRepr = asEventRepr(metadata, payloadRepr);
+
+        return jsonFor(eventRepr);
+    }
+    //endregion
+
+    //region > supporting methods
+    JsonRepresentation asEventRepr(EventMetadata metadata, final JsonRepresentation payloadRepr) {
+        final JsonRepresentation eventRepr = JsonRepresentation.newMap();
+        final JsonRepresentation metadataRepr = JsonRepresentation.newMap();
+        eventRepr.mapPut("metadata", metadataRepr);
+        metadataRepr.mapPut("id", metadata.getId());
+        metadataRepr.mapPut("transactionId", metadata.getTransactionId());
+        metadataRepr.mapPut("sequence", metadata.getSequence());
+        metadataRepr.mapPut("eventType", metadata.getEventType());
+        metadataRepr.mapPut("user", metadata.getUser());
+        metadataRepr.mapPut("timestamp", metadata.getTimestamp());
+        eventRepr.mapPut("payload", payloadRepr);
+        return eventRepr;
+    }
+
+    JsonRepresentation asPayloadRepr(final RendererContext rendererContext, EventPayload payload) {
+        final DomainObjectReprRenderer renderer = new DomainObjectReprRenderer(rendererContext, null, JsonRepresentation.newMap());
+        final ObjectAdapter objectAdapter = rendererContext.getAdapterManager().adapterFor(payload);
+        renderer.with(objectAdapter).asEventSerialization();
+        return renderer.render();
+    }
+
+    String jsonFor(final Object object) {
+        try {
+            return getJsonMapper().write(object);
+        } catch (final JsonGenerationException e) {
+            throw new RuntimeException(e);
+        } catch (final JsonMappingException e) {
+            throw new RuntimeException(e);
+        } catch (final IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private final static JsonMapper jsonMapper = JsonMapper.instance();
+
+    JsonMapper getJsonMapper() {
+        return jsonMapper;
+    }
+    //endregion
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-publishingeventserializer-ro/src/main/resources/images/PublishedEventJdo.png
----------------------------------------------------------------------
diff --git a/core/module-publishingeventserializer-ro/src/main/resources/images/PublishedEventJdo.png b/core/module-publishingeventserializer-ro/src/main/resources/images/PublishedEventJdo.png
new file mode 100644
index 0000000..cb3a325
Binary files /dev/null and b/core/module-publishingeventserializer-ro/src/main/resources/images/PublishedEventJdo.png differ

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/ApplicationSettingJdo.java
----------------------------------------------------------------------
diff --git a/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/ApplicationSettingJdo.java b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/ApplicationSettingJdo.java
new file mode 100644
index 0000000..22d5e77
--- /dev/null
+++ b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/ApplicationSettingJdo.java
@@ -0,0 +1,96 @@
+/*
+ *  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.settings;
+
+import javax.jdo.annotations.IdentityType;
+
+import org.apache.isis.applib.annotation.Named;
+import org.apache.isis.applib.services.settings.ApplicationSetting;
+import org.apache.isis.applib.services.settings.SettingType;
+import org.apache.isis.objectstore.jdo.applib.service.JdoColumnLength;
+
+@javax.jdo.annotations.PersistenceCapable(
+        identityType = IdentityType.APPLICATION,
+        table="IsisApplicationSetting")
+@javax.jdo.annotations.Queries({ 
+     @javax.jdo.annotations.Query(
+             name = "findByKey", language = "JDOQL", 
+             value = "SELECT "
+                     + "FROM org.apache.isis.objectstore.jdo.applib.service.settings.ApplicationSettingJdo "
+                     + "WHERE key == :key"),
+     @javax.jdo.annotations.Query(
+            name = "findAll", language = "JDOQL", 
+            value = "SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.settings.ApplicationSettingJdo "
+                    + "ORDER BY key")
+})
+@Named("Application Setting")
+public class ApplicationSettingJdo extends SettingAbstractJdo implements ApplicationSetting {
+
+
+    @javax.jdo.annotations.Column(length=JdoColumnLength.SettingAbstract.SETTING_KEY)
+    @javax.jdo.annotations.PrimaryKey
+    public String getKey() {
+        return super.getKey();
+    }
+    @Override
+    public void setKey(String key) {
+        super.setKey(key);
+    }
+
+    // //////////////////////////////////////
+
+    @javax.jdo.annotations.Column(length=JdoColumnLength.DESCRIPTION)
+    @javax.jdo.annotations.Persistent
+    @Override
+    public String getDescription() {
+        return super.getDescription();
+    }
+    @Override
+    public void setDescription(String description) {
+        super.setDescription(description);
+    }
+    
+    // //////////////////////////////////////
+
+    @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.SettingAbstract.VALUE_RAW)
+    @javax.jdo.annotations.Persistent
+    @Override
+    public String getValueRaw() {
+        return super.getValueRaw();
+    }
+    @Override
+    public void setValueRaw(String valueAsRaw) {
+        super.setValueRaw(valueAsRaw);
+    }
+    
+    // //////////////////////////////////////
+
+    @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.SettingAbstract.SETTING_TYPE)
+    @javax.jdo.annotations.Persistent
+    @Override
+    public SettingType getType() {
+        return super.getType();
+    }
+    @Override
+    public void setType(SettingType type) {
+        super.setType(type);
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/ApplicationSettingsServiceJdo.java
----------------------------------------------------------------------
diff --git a/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/ApplicationSettingsServiceJdo.java b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/ApplicationSettingsServiceJdo.java
new file mode 100644
index 0000000..014455a
--- /dev/null
+++ b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/ApplicationSettingsServiceJdo.java
@@ -0,0 +1,120 @@
+/*
+ *  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.settings;
+
+import java.util.List;
+
+import org.joda.time.LocalDate;
+
+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.MemberOrder;
+import org.apache.isis.applib.annotation.Named;
+import org.apache.isis.applib.annotation.Optional;
+import org.apache.isis.applib.query.QueryDefault;
+import org.apache.isis.applib.services.settings.ApplicationSetting;
+import org.apache.isis.applib.services.settings.ApplicationSettingsService;
+import org.apache.isis.applib.services.settings.ApplicationSettingsServiceRW;
+import org.apache.isis.applib.services.settings.SettingAbstract;
+import org.apache.isis.applib.services.settings.SettingType;
+
+/**
+ * An implementation of {@link ApplicationSettingsService} that persists settings
+ * as entities into a JDO-backed database.
+ */
+@Named("Application Settings")
+public class ApplicationSettingsServiceJdo extends AbstractService implements ApplicationSettingsServiceRW {
+
+    @ActionSemantics(Of.SAFE)
+    @Override
+    public ApplicationSetting find(@Named("Key") String key) {
+        return firstMatch(
+                new QueryDefault<ApplicationSettingJdo>(ApplicationSettingJdo.class, 
+                        "findByKey", 
+                        "key", key));
+    }
+
+    // //////////////////////////////////////
+
+    @ActionSemantics(Of.SAFE)
+    @MemberOrder(sequence="1")
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public List<ApplicationSetting> listAll() {
+        return (List)allMatches(
+                new QueryDefault<ApplicationSettingJdo>(ApplicationSettingJdo.class, 
+                        "findAll"));
+    }
+
+    // //////////////////////////////////////
+
+    @MemberOrder(sequence="10")
+    @Override
+    public ApplicationSetting newString(
+            @Named("Key") String key, 
+            @Named("Description") @Optional String description, 
+            @Named("Value") String value) {
+        return newSetting(key, description, SettingType.STRING, value);
+    }
+    @MemberOrder(sequence="11")
+    @Override
+    public ApplicationSettingJdo newInt(
+            @Named("Key") String key, 
+            @Named("Description") @Optional String description, 
+            @Named("Value") Integer value) {
+        return newSetting(key, description, SettingType.INT, value.toString());
+    }
+    @MemberOrder(sequence="12")
+    @Override
+    public ApplicationSettingJdo newLong(
+            @Named("Key") String key, 
+            @Named("Description") @Optional String description, 
+            @Named("Value") Long value) {
+        return newSetting(key, description, SettingType.LONG, value.toString());
+    }
+    @MemberOrder(sequence="13")
+    @Override
+    public ApplicationSettingJdo newLocalDate(
+            @Named("Key") String key, 
+            @Named("Description") @Optional String description, 
+            @Named("Value") LocalDate value) {
+        return newSetting(key, description, SettingType.LOCAL_DATE, value.toString(SettingAbstract.DATE_FORMATTER));
+    }
+    @MemberOrder(sequence="14")
+    @Override
+    public ApplicationSettingJdo newBoolean(
+            @Named("Key") String key, 
+            @Named("Description") @Optional String description, 
+            @Named("Value") @Optional Boolean value) {
+        return newSetting(key, description, SettingType.BOOLEAN, new Boolean(value != null && value).toString());
+    }
+
+    private ApplicationSettingJdo newSetting(
+            String key, String description, SettingType settingType, final String valueRaw) {
+        final ApplicationSettingJdo setting = newTransientInstance(ApplicationSettingJdo.class);
+        setting.setKey(key);
+        setting.setDescription(description);
+        setting.setValueRaw(valueRaw);
+        setting.setType(settingType);
+        persist(setting);
+        return setting;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/ApplicationSettingsServiceJdoHidden.java
----------------------------------------------------------------------
diff --git a/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/ApplicationSettingsServiceJdoHidden.java b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/ApplicationSettingsServiceJdoHidden.java
new file mode 100644
index 0000000..95512c0
--- /dev/null
+++ b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/ApplicationSettingsServiceJdoHidden.java
@@ -0,0 +1,81 @@
+/*
+ *  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.settings;
+
+import java.util.List;
+
+import org.joda.time.LocalDate;
+
+import org.apache.isis.applib.annotation.Hidden;
+import org.apache.isis.applib.annotation.Named;
+import org.apache.isis.applib.services.settings.ApplicationSetting;
+
+/**
+ * An implementation intended to be hidden in the UI, and delegated to by other services.
+ */
+public class ApplicationSettingsServiceJdoHidden extends ApplicationSettingsServiceJdo {
+
+    @Hidden
+    @Override
+    public ApplicationSetting find(@Named("Key") String key) {
+        return super.find(key);
+    }
+
+    // //////////////////////////////////////
+
+    @Hidden
+    @Override
+    public List<ApplicationSetting> listAll() {
+        return super.listAll();
+    }
+
+    // //////////////////////////////////////
+
+    @Hidden
+    @Override
+    public ApplicationSetting newString(String key, String description, String value) {
+        return super.newString(key, description, value);
+    }
+    
+    @Hidden
+    @Override
+    public ApplicationSettingJdo newInt(String key, String description, Integer value) {
+        return super.newInt(key, description, value);
+    }
+    
+    @Hidden
+    @Override
+    public ApplicationSettingJdo newLong(String key, String description, Long value) {
+        return super.newLong(key, description, value);
+    }
+    
+    @Hidden
+    @Override
+    public ApplicationSettingJdo newLocalDate(String key, String description, LocalDate value) {
+        return super.newLocalDate(key, description, value);
+    }
+    
+    @Hidden
+    @Override
+    public ApplicationSettingJdo newBoolean(String key, String description, Boolean value) {
+        return super.newBoolean(key, description, value);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/SettingAbstractJdo.java
----------------------------------------------------------------------
diff --git a/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/SettingAbstractJdo.java b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/SettingAbstractJdo.java
new file mode 100644
index 0000000..8e6f8da
--- /dev/null
+++ b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/SettingAbstractJdo.java
@@ -0,0 +1,190 @@
+/*
+ *  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.settings;
+
+import javax.jdo.annotations.PersistenceCapable;
+
+import org.joda.time.LocalDate;
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.Named;
+import org.apache.isis.applib.annotation.Optional;
+import org.apache.isis.applib.services.settings.SettingType;
+
+/**
+ * Factors out common implementation; however this is annotated with {@link PersistenceCapable},
+ * so that each subclass is its own root entity.
+ */
+public abstract class SettingAbstractJdo extends org.apache.isis.applib.services.settings.SettingAbstract implements org.apache.isis.applib.services.settings.ApplicationSetting {
+
+    private String key;
+
+    @javax.jdo.annotations.Column(allowsNull="false")
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(final String key) {
+        this.key = key;
+    }
+
+    // //////////////////////////////////////
+
+    private String description;
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(final String description) {
+        this.description = description;
+    }
+
+    @MemberOrder(name="Description", sequence="1")
+    @Named("Update")
+    public SettingAbstractJdo updateDescription(@Named("Description") @Optional String description) {
+        setDescription(description);
+        return this;
+    }
+    public String default0UpdateDescription() {
+        return getDescription();
+    }
+    
+    // //////////////////////////////////////
+
+    private SettingType type;
+
+    @javax.jdo.annotations.Column(allowsNull="false")
+    public SettingType getType() {
+        return type;
+    }
+
+    public void setType(final SettingType type) {
+        this.type = type;
+    }
+
+    // //////////////////////////////////////
+
+    private String valueRaw;
+
+    @javax.jdo.annotations.Column(allowsNull="false")
+    public String getValueRaw() {
+        return valueRaw;
+    }
+
+    public void setValueRaw(final String valueAsRaw) {
+        this.valueRaw = valueAsRaw;
+    }
+
+    // //////////////////////////////////////
+    
+    @MemberOrder(name="ValueAsString", sequence="1")
+    @Named("Update")
+    public SettingAbstractJdo updateAsString(@Named("Value") String value) {
+        setValueRaw(value);
+        return this;
+    }
+    public String default0UpdateAsString() {
+        return getValueAsString();
+    }
+    public boolean hideUpdateAsString() {
+        return typeIsNot(SettingType.STRING);
+    }
+    
+    @MemberOrder(name="ValueAsInt", sequence="1")
+    @Named("Update")
+    public SettingAbstractJdo updateAsInt(@Named("Value") Integer value) {
+        setValueRaw(value.toString());
+        return this;
+    }
+    public Integer default0UpdateAsInt() {
+        return getValueAsInt();
+    }
+    public boolean hideUpdateAsInt() {
+        return typeIsNot(SettingType.INT);
+    }
+    
+    @MemberOrder(name="ValueAsLong", sequence="1")
+    @Named("Update")
+    public SettingAbstractJdo updateAsLong(@Named("Value") Long value) {
+        setValueRaw(value.toString());
+        return this;
+    }
+    public Long default0UpdateAsLong() {
+        return getValueAsLong();
+    }
+    public boolean hideUpdateAsLong() {
+        return typeIsNot(SettingType.LONG);
+    }
+    
+    @MemberOrder(name="ValueAsLocalDate", sequence="1")
+    @Named("Update")
+    public SettingAbstractJdo updateAsLocalDate(@Named("Value") LocalDate value) {
+        setValueRaw(value.toString(DATE_FORMATTER));
+        return this;
+    }
+    public LocalDate default0UpdateAsLocalDate() {
+        return getValueAsLocalDate();
+    }
+    public boolean hideUpdateAsLocalDate() {
+        return typeIsNot(SettingType.LOCAL_DATE);
+    }
+
+    @MemberOrder(name="ValueAsBoolean", sequence="1")
+    @Named("Update")
+    public SettingAbstractJdo updateAsBoolean(@Named("Value") Boolean value) {
+        setValueRaw(value.toString());
+        return this;
+    }
+    public Boolean default0UpdateAsBoolean() {
+        return getValueAsBoolean();
+    }
+    public boolean hideUpdateAsBoolean() {
+        return typeIsNot(SettingType.BOOLEAN);
+    }
+    
+    // //////////////////////////////////////
+    
+    
+    public SettingAbstractJdo delete(
+            @Named("Are you sure?") @Optional Boolean confirm) {
+        if(confirm == null || !confirm) {
+            container.informUser("Setting NOT deleted");
+            return this;
+        }
+        container.remove(this);
+        container.informUser("Setting deleted");
+        return null;
+    }
+    
+
+    
+ 
+    // //////////////////////////////////////
+    
+    private DomainObjectContainer container;
+
+    public void setDomainObjectContainer(final DomainObjectContainer container) {
+        this.container = container;
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/UserSettingJdo.java
----------------------------------------------------------------------
diff --git a/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/UserSettingJdo.java b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/UserSettingJdo.java
new file mode 100644
index 0000000..f1ff4b2
--- /dev/null
+++ b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/UserSettingJdo.java
@@ -0,0 +1,129 @@
+/*
+ *  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.settings;
+
+
+import javax.jdo.annotations.IdentityType;
+
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.Named;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.services.settings.SettingType;
+import org.apache.isis.applib.services.settings.UserSetting;
+import org.apache.isis.objectstore.jdo.applib.service.JdoColumnLength;
+
+@javax.jdo.annotations.PersistenceCapable(
+        identityType = IdentityType.APPLICATION, 
+        objectIdClass=UserSettingJdoPK.class,
+        table="IsisUserSetting")
+@javax.jdo.annotations.Queries({ 
+    @javax.jdo.annotations.Query(
+            name = "findByUserAndKey", language = "JDOQL", 
+            value = "SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.settings.UserSettingJdo "
+                    + "WHERE user == :user "
+                    + "&& key == :key ") 
+    ,@javax.jdo.annotations.Query(
+            name = "findByUser", language = "JDOQL", 
+            value = "SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.settings.UserSettingJdo "
+                    + "WHERE user == :user "
+                    + "ORDER BY key") 
+    ,@javax.jdo.annotations.Query(
+            name = "findAll", language = "JDOQL", 
+            value = "SELECT "
+                    + "FROM org.apache.isis.objectstore.jdo.applib.service.settings.UserSettingJdo "
+                    + "ORDER BY user, key") 
+})
+// can't see how to specify this order in the primary key; however HSQLDB objects :-(
+//@javax.jdo.annotations.Unique(name="USER_KEY_IDX", members={"user","key"}) 
+@Named("User Setting")
+public class UserSettingJdo extends SettingAbstractJdo implements UserSetting {
+
+    
+    private String user;
+
+    @javax.jdo.annotations.Column(length=JdoColumnLength.USER_NAME)
+    @javax.jdo.annotations.PrimaryKey
+    @Title(sequence="5", append=": ")
+    @MemberOrder(sequence = "5")
+    public String getUser() {
+        return user;
+    }
+
+    public void setUser(final String user) {
+        this.user = user;
+    }
+
+    // //////////////////////////////////////
+
+    @javax.jdo.annotations.Column(length=JdoColumnLength.SettingAbstract.SETTING_KEY)
+    @javax.jdo.annotations.PrimaryKey
+    @Title(sequence="10")
+    @Override
+    public String getKey() {
+        return super.getKey();
+    }
+    @Override
+    public void setKey(String key) {
+        super.setKey(key);
+    }
+
+    // //////////////////////////////////////
+
+    @javax.jdo.annotations.Column(length=JdoColumnLength.DESCRIPTION)
+    @javax.jdo.annotations.Persistent
+    @Override
+    public String getDescription() {
+        return super.getDescription();
+    }
+    @Override
+    public void setDescription(String description) {
+        super.setDescription(description);
+    }
+    
+    // //////////////////////////////////////
+
+    @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.SettingAbstract.VALUE_RAW)
+    @javax.jdo.annotations.Persistent
+    @Title(prepend=" = ", sequence="30")
+    @Override
+    public String getValueRaw() {
+        return super.getValueRaw();
+    }
+    @Override
+    public void setValueRaw(String valueAsRaw) {
+        super.setValueRaw(valueAsRaw);
+    }
+    
+    // //////////////////////////////////////
+
+    @javax.jdo.annotations.Column(allowsNull="false", length=JdoColumnLength.SettingAbstract.SETTING_TYPE)
+    @javax.jdo.annotations.Persistent
+    @Override
+    public SettingType getType() {
+        return super.getType();
+    }
+    @Override
+    public void setType(SettingType type) {
+        super.setType(type);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/UserSettingJdoPK.java
----------------------------------------------------------------------
diff --git a/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/UserSettingJdoPK.java b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/UserSettingJdoPK.java
new file mode 100644
index 0000000..13a20ed
--- /dev/null
+++ b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/UserSettingJdoPK.java
@@ -0,0 +1,90 @@
+/**
+ *  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.settings;
+
+import java.io.Serializable;
+import java.util.StringTokenizer;
+
+/**
+ * @see http://www.datanucleus.org/products/datanucleus/jdo/primary_key.html
+ */
+public class UserSettingJdoPK implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    
+    public String user;
+    public String key;
+    
+    public UserSettingJdoPK ()
+    {
+    }
+
+    public String getUser() {
+        return user;
+    }
+
+    public void setUser(String user) {
+        this.user = user;
+    }
+
+    public String getKey() {
+        return key;
+    }
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+
+    /**
+     * Constructor accepting same input as generated by toString().
+     */
+    public UserSettingJdoPK(String value) 
+    {
+        StringTokenizer token = new StringTokenizer (value, ";;");
+        token.nextToken();               // className
+        this.setUser(token.nextToken());   // user
+        this.setKey(token.nextToken());    // key
+    }
+
+    public boolean equals(Object obj)
+    {
+        if (obj == this)
+        {
+            return true;
+        }
+        if (!(obj instanceof UserSettingJdoPK))
+        {
+            return false;
+        }
+        UserSettingJdoPK c = (UserSettingJdoPK)obj;
+
+        return getUser().equals(c.getUser()) && getKey().equals(c.getKey());
+    }
+
+    public int hashCode ()
+    {
+        return this.getUser().hashCode() ^ this.getKey().hashCode();
+    }
+
+    public String toString ()
+    {
+        // Give output expected by String constructor
+        return this.getClass().getName() + ";;"  + this.getUser() + ";;" + this.getKey();
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/e3c20592/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/UserSettingsServiceJdo.java
----------------------------------------------------------------------
diff --git a/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/UserSettingsServiceJdo.java b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/UserSettingsServiceJdo.java
new file mode 100644
index 0000000..21ba8b9
--- /dev/null
+++ b/core/module-settings/impl-jdo/src/main/java/org/apache/isis/objectstore/jdo/applib/service/settings/UserSettingsServiceJdo.java
@@ -0,0 +1,170 @@
+/*
+ *  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.settings;
+
+import java.util.List;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import org.joda.time.LocalDate;
+
+import org.apache.isis.applib.AbstractService;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.Named;
+import org.apache.isis.applib.annotation.Optional;
+import org.apache.isis.applib.query.QueryDefault;
+import org.apache.isis.applib.services.settings.SettingAbstract;
+import org.apache.isis.applib.services.settings.SettingType;
+import org.apache.isis.applib.services.settings.UserSetting;
+import org.apache.isis.applib.services.settings.UserSettingsService;
+import org.apache.isis.applib.services.settings.UserSettingsServiceRW;
+
+/**
+ * An implementation of {@link UserSettingsService} that persists settings
+ * as entities into a JDO-backed database.
+ */
+@Named("User Settings")
+public class UserSettingsServiceJdo extends AbstractService implements UserSettingsServiceRW {
+
+    @Override
+    public UserSetting find(
+            @Named("User") String user, 
+            @Named("Key") String key) {
+        return firstMatch(
+                new QueryDefault<UserSettingJdo>(UserSettingJdo.class, 
+                        "findByUserAndKey", 
+                        "user",user,
+                        "key", key));
+    }
+
+
+    // //////////////////////////////////////
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public List<UserSetting> listAllFor(String user) {
+        return (List)allMatches(
+                new QueryDefault<UserSettingJdo>(UserSettingJdo.class, 
+                        "findByUser", 
+                        "user", user));
+    }
+    public List<String> choices0ListAllFor() {
+        return existingUsers();
+    }
+
+    private List<String> existingUsers() {
+        final List<UserSetting> listAll = listAll();
+        return Lists.newArrayList(Sets.newTreeSet(Iterables.transform(listAll, GET_USER)));
+    }
+
+    private final static Function<UserSetting, String> GET_USER = new Function<UserSetting, String>() {
+        public String apply(UserSetting input) {
+            return input.getUser();
+        }
+    };
+
+    // //////////////////////////////////////
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public List<UserSetting> listAll() {
+        return (List)allMatches(
+                new QueryDefault<UserSettingJdo>(UserSettingJdo.class, 
+                        "findAll"));
+    }
+
+
+    // //////////////////////////////////////
+    
+    @MemberOrder(sequence="10")
+    public UserSettingJdo newString(
+            @Named("User") String user, 
+            @Named("Key") String key, 
+            @Named("Description") @Optional String description, 
+            @Named("Value") String value) {
+        return newSetting(user, key, description, SettingType.STRING, value);
+    }
+    public String default0NewString() {
+        return getContainer().getUser().getName();
+    }
+
+    @MemberOrder(sequence="11")
+    public UserSettingJdo newInt(
+            @Named("User") String user, 
+            @Named("Key") String key, 
+            @Named("Description") @Optional String description, 
+            @Named("Value") Integer value) {
+        return newSetting(user, key, description, SettingType.INT, value.toString());
+    }
+    public String default0NewInt() {
+        return getContainer().getUser().getName();
+    }
+
+    @MemberOrder(sequence="12")
+    public UserSettingJdo newLong(
+            @Named("User") String user, 
+            @Named("Key") String key, 
+            @Named("Description") @Optional String description, 
+            @Named("Value") Long value) {
+        return newSetting(user, key, description, SettingType.LONG, value.toString());
+    }
+    public String default0NewLong() {
+        return getContainer().getUser().getName();
+    }
+
+    @MemberOrder(sequence="13")
+    public UserSettingJdo newLocalDate(
+            @Named("User") String user, 
+            @Named("Key") String key, 
+            @Named("Description") @Optional String description, 
+            @Named("Value") LocalDate value) {
+        return newSetting(user, key, description, SettingType.LOCAL_DATE, value.toString(SettingAbstract.DATE_FORMATTER));
+    }
+    public String default0NewLocalDate() {
+        return getContainer().getUser().getName();
+    }
+
+    @MemberOrder(sequence="14")
+    public UserSettingJdo newBoolean(
+            @Named("User") String user, 
+            @Named("Key") String key, 
+            @Named("Description") @Optional String description, 
+            @Named("Value") @Optional Boolean value) {
+        return newSetting(user, key, description, SettingType.BOOLEAN, new Boolean(value != null && value).toString());
+    }
+    public String default0NewBoolean() {
+        return getContainer().getUser().getName();
+    }
+
+    private UserSettingJdo newSetting(
+            String user, String key, String description, SettingType settingType, final String valueRaw) {
+        final UserSettingJdo setting = newTransientInstance(UserSettingJdo.class);
+        setting.setUser(user);
+        setting.setKey(key);
+        setting.setType(settingType);
+        setting.setDescription(description);
+        setting.setValueRaw(valueRaw);
+        persist(setting);
+        return setting;
+    }
+    
+
+}