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 2013/02/08 19:29:52 UTC

[14/32] ISIS-323: lots more refactoring of RO

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/impl/src/test/java/org/apache/isis/viewer/restfulobjects/viewer/resources/domainobjects/JsonValueEncoderTest_asObject.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/impl/src/test/java/org/apache/isis/viewer/restfulobjects/viewer/resources/domainobjects/JsonValueEncoderTest_asObject.java b/component/viewer/restfulobjects/impl/src/test/java/org/apache/isis/viewer/restfulobjects/viewer/resources/domainobjects/JsonValueEncoderTest_asObject.java
deleted file mode 100644
index 38f5841..0000000
--- a/component/viewer/restfulobjects/impl/src/test/java/org/apache/isis/viewer/restfulobjects/viewer/resources/domainobjects/JsonValueEncoderTest_asObject.java
+++ /dev/null
@@ -1,248 +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.viewer.restfulobjects.viewer.resources.domainobjects;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-
-import java.math.BigDecimal;
-import java.math.BigInteger;
-
-import org.jmock.Expectations;
-import org.jmock.Mockery;
-import org.jmock.integration.junit4.JMock;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.core.metamodel.facetapi.Facet;
-import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
-import org.apache.isis.viewer.restfulobjects.viewer.resources.domainobjects.JsonValueEncoder;
-
-@RunWith(JMock.class)
-public class JsonValueEncoderTest_asObject {
-
-    private final Mockery context = new JUnit4Mockery();
-
-    private JsonValueEncoder jsonValueEncoder;
-    private JsonRepresentation representation;
-    private ObjectAdapter objectAdapter;
-    private ObjectSpecification objectSpec;
-
-    private EncodableFacet encodableFacet;
-    private Object encoded;
-
-    @Before
-    public void setUp() throws Exception {
-        objectAdapter = context.mock(ObjectAdapter.class);
-        objectSpec = context.mock(ObjectSpecification.class);
-
-        context.checking(new Expectations() {
-            {
-                allowing(objectAdapter).getSpecification();
-                will(returnValue(objectSpec));
-            }
-        });
-        encodableFacet = context.mock(EncodableFacet.class);
-
-        encoded = new Object();
-
-        jsonValueEncoder = new JsonValueEncoder();
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void whenAdapterIsNull() throws Exception {
-        jsonValueEncoder.asObject(null);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void whenObjectAdapterIsNotSpecialCaseAndSpecIsNotEncodable() throws Exception {
-        allowingObjectSpecCorrespondingClassIs(String.class);
-        allowingObjectSpecHas(EncodableFacet.class, null);
-        jsonValueEncoder.asObject(objectAdapter);
-    }
-
-    @Test
-    public void whenBooleanPrimitive() throws Exception {
-        whenBoolean(boolean.class);
-    }
-
-    @Test
-    public void whenBooleanWrapper() throws Exception {
-        whenBoolean(Boolean.class);
-    }
-
-    private void whenBoolean(final Class<?> cls) {
-        allowingObjectSpecCorrespondingClassIs(cls);
-        allowingObjectSpecHas(EncodableFacet.class, encodableFacet);
-        never(encodableFacet);
-        context.checking(new Expectations() {
-            {
-                one(objectAdapter).getObject();
-                will(returnValue(true));
-            }
-        });
-        assertEquals(true, jsonValueEncoder.asObject(objectAdapter));
-    }
-
-    @Test
-    public void whenIntegerPrimitive() throws Exception {
-        whenInteger(int.class);
-    }
-
-    @Test
-    public void whenIntegerWrapper() throws Exception {
-        whenInteger(Integer.class);
-    }
-
-    private void whenInteger(final Class<?> cls) {
-        allowingObjectSpecCorrespondingClassIs(cls);
-        allowingObjectSpecHas(EncodableFacet.class, encodableFacet);
-        never(encodableFacet);
-        context.checking(new Expectations() {
-            {
-                one(objectAdapter).getObject();
-                will(returnValue(123));
-            }
-        });
-        assertEquals(123, jsonValueEncoder.asObject(objectAdapter));
-    }
-
-    @Test
-    public void whenLongPrimitive() throws Exception {
-        whenLong(long.class);
-    }
-
-    @Test
-    public void whenLongWrapper() throws Exception {
-        whenLong(Long.class);
-    }
-
-    private void whenLong(final Class<?> cls) {
-        allowingObjectSpecCorrespondingClassIs(cls);
-        allowingObjectSpecHas(EncodableFacet.class, encodableFacet);
-        never(encodableFacet);
-        context.checking(new Expectations() {
-            {
-                one(objectAdapter).getObject();
-                will(returnValue(123456789L));
-            }
-        });
-        assertEquals(123456789L, jsonValueEncoder.asObject(objectAdapter));
-    }
-
-    @Test
-    public void whenDoublePrimitive() throws Exception {
-        whenDouble(double.class);
-    }
-
-    @Test
-    public void whenDoubleWrapper() throws Exception {
-        whenDouble(Double.class);
-    }
-
-    private void whenDouble(final Class<?> cls) {
-        allowingObjectSpecCorrespondingClassIs(cls);
-        allowingObjectSpecHas(EncodableFacet.class, encodableFacet);
-        never(encodableFacet);
-        context.checking(new Expectations() {
-            {
-                one(objectAdapter).getObject();
-                will(returnValue(12345.6789));
-            }
-        });
-        assertEquals(12345.6789, jsonValueEncoder.asObject(objectAdapter));
-    }
-
-    @Test
-    public void whenBigInteger() throws Exception {
-        allowingObjectSpecCorrespondingClassIs(BigInteger.class);
-        allowingObjectSpecHas(EncodableFacet.class, encodableFacet);
-        never(encodableFacet);
-        final BigInteger value = new BigInteger("123456789012345");
-        context.checking(new Expectations() {
-
-            {
-                one(objectAdapter).getObject();
-                will(returnValue(value));
-            }
-        });
-        assertEquals(value, jsonValueEncoder.asObject(objectAdapter));
-    }
-
-    @Test
-    public void whenBigDecimal() throws Exception {
-        allowingObjectSpecCorrespondingClassIs(BigDecimal.class);
-        allowingObjectSpecHas(EncodableFacet.class, encodableFacet);
-        never(encodableFacet);
-        final BigDecimal value = new BigDecimal("1234567890.1234567890");
-        context.checking(new Expectations() {
-
-            {
-                one(objectAdapter).getObject();
-                will(returnValue(value));
-            }
-        });
-        assertEquals(value, jsonValueEncoder.asObject(objectAdapter));
-    }
-
-    @Test
-    public void whenString() throws Exception {
-        allowingObjectSpecCorrespondingClassIs(String.class);
-        allowingObjectSpecHas(EncodableFacet.class, encodableFacet);
-        context.checking(new Expectations() {
-            {
-                one(encodableFacet).toEncodedString(objectAdapter);
-                will(returnValue("encodedString"));
-            }
-        });
-        assertSame("encodedString", jsonValueEncoder.asObject(objectAdapter));
-    }
-
-    private void allowingObjectSpecCorrespondingClassIs(final Class<?> result) {
-        context.checking(new Expectations() {
-            {
-                allowing(objectSpec).getCorrespondingClass();
-                will(returnValue(result));
-            }
-        });
-    }
-
-    private <T extends Facet> void allowingObjectSpecHas(final Class<T> facetClass, final T encodableFacet) {
-        context.checking(new Expectations() {
-            {
-                allowing(objectSpec).getFacet(facetClass);
-                will(returnValue(encodableFacet));
-            }
-        });
-    }
-
-    private void never(final EncodableFacet encodableFacet2) {
-        context.checking(new Expectations() {
-            {
-                never(encodableFacet2);
-            }
-        });
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/impl/src/test/java/org/apache/isis/viewer/restfulobjects/viewer/util/MapUtilsTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/impl/src/test/java/org/apache/isis/viewer/restfulobjects/viewer/util/MapUtilsTest.java b/component/viewer/restfulobjects/impl/src/test/java/org/apache/isis/viewer/restfulobjects/viewer/util/MapUtilsTest.java
deleted file mode 100644
index 705c7e9..0000000
--- a/component/viewer/restfulobjects/impl/src/test/java/org/apache/isis/viewer/restfulobjects/viewer/util/MapUtilsTest.java
+++ /dev/null
@@ -1,51 +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.viewer.restfulobjects.viewer.util;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-import java.util.Map;
-
-import org.junit.Test;
-
-import org.apache.isis.viewer.restfulobjects.viewer.util.MapUtils;
-
-public class MapUtilsTest {
-
-    @Test
-    public void happyCase() throws Exception {
-        final Map<String, String> map = MapUtils.mapOf("foo", "bar", "foz", "boz");
-        assertThat(map.get("foo"), is("bar"));
-        assertThat(map.get("foz"), is("boz"));
-        assertThat(map.size(), is(2));
-    }
-
-    @Test
-    public void emptyList() throws Exception {
-        final Map<String, String> map = MapUtils.mapOf();
-        assertThat(map.size(), is(0));
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void uneven() throws Exception {
-        MapUtils.mapOf("foo");
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/impl/src/test/java/org/apache/isis/viewer/restfulobjects/viewer/util/UrlParserUtilsTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/impl/src/test/java/org/apache/isis/viewer/restfulobjects/viewer/util/UrlParserUtilsTest.java b/component/viewer/restfulobjects/impl/src/test/java/org/apache/isis/viewer/restfulobjects/viewer/util/UrlParserUtilsTest.java
deleted file mode 100644
index 912f18a..0000000
--- a/component/viewer/restfulobjects/impl/src/test/java/org/apache/isis/viewer/restfulobjects/viewer/util/UrlParserUtilsTest.java
+++ /dev/null
@@ -1,62 +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.viewer.restfulobjects.viewer.util;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-
-import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
-import org.apache.isis.viewer.restfulobjects.viewer.util.UrlParserUtils;
-
-public class UrlParserUtilsTest {
-
-    @Test
-    public void oidFromLink() throws Exception {
-        final JsonRepresentation link = JsonRepresentation.newMap();
-        link.mapPut("href", "http://localhost/objects/OID:1");
-        final String oidFromHref = UrlParserUtils.oidFromLink(link);
-        assertEquals("OID:1", oidFromHref);
-    }
-
-    @Test
-    public void domainTypeFromLink() throws Exception {
-        final JsonRepresentation link = JsonRepresentation.newMap();
-        link.mapPut("href", "http://localhost/domainTypes/com.mycompany.myapp.Customer");
-        final String oidFromHref = UrlParserUtils.domainTypeFrom(link);
-        assertEquals("com.mycompany.myapp.Customer", oidFromHref);
-    }
-
-    @Test
-    public void domainTypeFromLinkTrailingSlash() throws Exception {
-        final JsonRepresentation link = JsonRepresentation.newMap();
-        link.mapPut("href", "http://localhost/domainTypes/com.mycompany.myapp.Customer/");
-        final String oidFromHref = UrlParserUtils.domainTypeFrom(link);
-        assertEquals("com.mycompany.myapp.Customer", oidFromHref);
-    }
-
-    @Test
-    public void domainTypeFromLinkFollowingStuff() throws Exception {
-        final JsonRepresentation link = JsonRepresentation.newMap();
-        link.mapPut("href", "http://localhost/domainTypes/com.mycompany.myapp.Customer/otherStuffHere");
-        final String oidFromHref = UrlParserUtils.domainTypeFrom(link);
-        assertEquals("com.mycompany.myapp.Customer", oidFromHref);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/jaxrs/pom.xml
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/jaxrs/pom.xml b/component/viewer/restfulobjects/jaxrs/pom.xml
new file mode 100644
index 0000000..e3b5e8d
--- /dev/null
+++ b/component/viewer/restfulobjects/jaxrs/pom.xml
@@ -0,0 +1,85 @@
+<?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.viewer</groupId>
+		<artifactId>isis-viewer-restfulobjects</artifactId>
+		<version>2.0.0-SNAPSHOT</version>
+	</parent>
+	
+	<artifactId>isis-viewer-restfulobjects-jaxrs</artifactId>
+	<name>Isis RestfulObjects Viewer jax-rs Resources</name>
+
+	<properties>
+		<siteBaseDir>..</siteBaseDir>
+		<relativeUrl>applib/</relativeUrl>
+	</properties>
+
+    <!-- used in Site generation for relative references. -->
+    <url>http://isis.apache.org/${relativeUrl}</url>
+
+	<dependencies>
+		<dependency>
+            <groupId>org.apache.isis.viewer</groupId>
+	        <artifactId>isis-viewer-restfulobjects-applib</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.jboss.resteasy</groupId>
+			<artifactId>resteasy-jaxrs</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-core-asl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-mapper-asl</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-metamodel</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-unittestsupport</artifactId>
+            <scope>test</scope>
+        </dependency>
+	    	
+	</dependencies>
+
+
+    <repositories>
+        <!-- The artifact in the maven repository has been moved and causes a build failure. Using this setting gets the artifact allowing the build to work.  This fix should hopefully be temporary -->
+        <repository>
+          <id>java.net</id>
+          <url>http://repo1.maven.org/maven2</url>
+            <snapshots>
+              <enabled>false</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
+
+</project>

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/pom.xml
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/pom.xml b/component/viewer/restfulobjects/pom.xml
index e6d286e..93d7744 100644
--- a/component/viewer/restfulobjects/pom.xml
+++ b/component/viewer/restfulobjects/pom.xml
@@ -30,7 +30,7 @@
 
 	<groupId>org.apache.isis.viewer</groupId>
 	<artifactId>isis-viewer-restfulobjects</artifactId>
-    <version>1.0.1-SNAPSHOT</version>
+    <version>2.0.0-SNAPSHOT</version>
 
 	<name>Isis RestfulObjects Viewer</name>
 
@@ -119,24 +119,36 @@
 	   		<dependency>
 				<groupId>org.apache.isis.viewer</groupId>
 				<artifactId>isis-viewer-restfulobjects-applib</artifactId>
-				<version>1.0.1-SNAPSHOT</version>
+				<version>2.0.0-SNAPSHOT</version>
 			</dependency>
 	   		<dependency>
 				<groupId>org.apache.isis.viewer</groupId>
 				<artifactId>isis-viewer-restfulobjects-applib</artifactId>
-				<version>1.0.1-SNAPSHOT</version>
+				<version>2.0.0-SNAPSHOT</version>
                 <type>test-jar</type>
                 <scope>test</scope>
 			</dependency>
 	   		<dependency>
 				<groupId>org.apache.isis.viewer</groupId>
-				<artifactId>isis-viewer-restfulobjects-impl</artifactId>
-				<version>1.0.1-SNAPSHOT</version>
+				<artifactId>isis-viewer-restfulobjects-rendering</artifactId>
+				<version>2.0.0-SNAPSHOT</version>
 			</dependency>
 	   		<dependency>
 				<groupId>org.apache.isis.viewer</groupId>
-				<artifactId>isis-viewer-restfulobjects-impl</artifactId>
-				<version>1.0.1-SNAPSHOT</version>
+				<artifactId>isis-viewer-restfulobjects-rendering</artifactId>
+				<version>2.0.0-SNAPSHOT</version>
+                <type>test-jar</type>
+                <scope>test</scope>
+			</dependency>
+	   		<dependency>
+				<groupId>org.apache.isis.viewer</groupId>
+				<artifactId>isis-viewer-restfulobjects-server</artifactId>
+				<version>2.0.0-SNAPSHOT</version>
+			</dependency>
+	   		<dependency>
+				<groupId>org.apache.isis.viewer</groupId>
+				<artifactId>isis-viewer-restfulobjects-server</artifactId>
+				<version>2.0.0-SNAPSHOT</version>
                 <type>test-jar</type>
                 <scope>test</scope>
 			</dependency>
@@ -147,7 +159,9 @@
 
     <modules>
 	    <module>applib</module>
-	    <module>impl</module>
+	    <module>jaxrs</module>
+	    <module>rendering</module>
+	    <module>server</module>
 	    <module>tck</module>
     </modules>
 </project>

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/pom.xml
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/pom.xml b/component/viewer/restfulobjects/rendering/pom.xml
new file mode 100644
index 0000000..9e5b3e2
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/pom.xml
@@ -0,0 +1,62 @@
+<?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.viewer</groupId>
+		<artifactId>isis-viewer-restfulobjects</artifactId>
+		<version>2.0.0-SNAPSHOT</version>
+	</parent>
+
+	<artifactId>isis-viewer-restfulobjects-rendering</artifactId>
+	<name>Isis RestfulObjects Viewer Rendering</name>
+
+	<properties>
+		<siteBaseDir>..</siteBaseDir>
+		<relativeUrl>viewer/</relativeUrl>
+	</properties>
+
+    <!-- used in Site generation for relative references. -->
+    <url>http://isis.apache.org/${relativeUrl}</url>
+
+	<dependencies>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-unittestsupport</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+		<dependency>
+			<groupId>org.apache.isis.core</groupId>
+			<artifactId>isis-core-metamodel</artifactId>
+		</dependency>
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-runtime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.isis.viewer</groupId>
+            <artifactId>isis-viewer-restfulobjects-applib</artifactId>
+        </dependency>
+
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/LinkBuilder.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/LinkBuilder.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/LinkBuilder.java
new file mode 100644
index 0000000..a120f59
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/LinkBuilder.java
@@ -0,0 +1,94 @@
+/**
+ *  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;
+
+
+import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
+import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
+import org.apache.isis.viewer.restfulobjects.applib.RoHttpMethod;
+import org.apache.isis.viewer.restfulobjects.applib.links.Rel;
+
+import com.google.common.net.MediaType;
+
+public final class LinkBuilder {
+
+    public static LinkBuilder newBuilder(final RendererContext resourceContext, final Rel rel, final RepresentationType representationType, final String hrefFormat, final Object... hrefArgs) {
+        return newBuilder(resourceContext, rel, representationType.getMediaType(), hrefFormat, hrefArgs);
+    }
+
+    public static LinkBuilder newBuilder(final RendererContext resourceContext, final Rel rel, final MediaType mediaType, final String hrefFormat, final Object... hrefArgs) {
+        return new LinkBuilder(resourceContext, rel, String.format(hrefFormat, hrefArgs), mediaType);
+    }
+
+    private final RendererContext resourceContext;
+    private final JsonRepresentation representation = JsonRepresentation.newMap();
+
+    private final Rel rel;
+    private final String href;
+    private final MediaType mediaType;
+
+    private RoHttpMethod method = RoHttpMethod.GET;
+    private String title;
+    private JsonRepresentation arguments;
+    private JsonRepresentation value;
+    private String id;
+
+    protected LinkBuilder(final RendererContext resourceContext, final Rel rel, final String href, final MediaType mediaType) {
+        this.resourceContext = resourceContext;
+        this.rel = rel;
+        this.href = href;
+        this.mediaType = mediaType;
+    }
+
+    public LinkBuilder withHttpMethod(final RoHttpMethod method) {
+        this.method = method;
+        return this;
+    }
+
+    public LinkBuilder withTitle(final String title) {
+        this.title = title;
+        return this;
+    }
+
+    public LinkBuilder withArguments(final JsonRepresentation arguments) {
+        this.arguments = arguments;
+        return this;
+    }
+
+    public LinkBuilder withId(final String id) {
+        this.id = id;
+        return this;
+    }
+
+    public LinkBuilder withValue(final JsonRepresentation value) {
+        this.value = value;
+        return this;
+    }
+
+    public JsonRepresentation build() {
+        representation.mapPut("id", id);
+        representation.mapPut("rel", rel.getName());
+        representation.mapPut("href", resourceContext.urlFor(href));
+        representation.mapPut("method", method);
+        representation.mapPut("type", mediaType.toString());
+        representation.mapPut("title", title);
+        representation.mapPut("arguments", arguments);
+        representation.mapPut("value", value);
+        return representation;
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/LinkFollower.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/LinkFollower.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/LinkFollower.java
new file mode 100644
index 0000000..3818ff8
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/LinkFollower.java
@@ -0,0 +1,125 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.restfulobjects.rendering;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
+import org.apache.isis.viewer.restfulobjects.applib.util.PathNode;
+import org.apache.isis.viewer.restfulobjects.rendering.util.GraphUtil;
+
+@SuppressWarnings({ "rawtypes", "unchecked" })
+public final class LinkFollower {
+
+    public final static LinkFollower create(final List<List<String>> links) {
+        final Map<PathNode, Map> graph = GraphUtil.asGraph(links);
+        return new LinkFollower(graph, Mode.FOLLOWING, PathNode.NULL);
+    }
+
+    private enum Mode {
+        FOLLOWING, TERMINATED;
+    }
+
+    private final Map<PathNode, Map> graph;
+    private final Mode mode;
+    private final PathNode root;
+
+    private LinkFollower(final Map<PathNode, Map> graph, final Mode mode, final PathNode root) {
+        this.graph = graph;
+        this.mode = mode;
+        this.root = root;
+    }
+
+    /**
+     * A little algebra...
+     */
+    public LinkFollower follow(final String pathTemplate, final Object... args) {
+        final String path = String.format(pathTemplate, args);
+        if (path == null) {
+            return terminated(PathNode.NULL);
+        }
+        if (mode == Mode.TERMINATED) {
+            return terminated(this.root);
+        }
+        final PathNode node = PathNode.parse(path);
+        if (mode == Mode.FOLLOWING) {
+            final Map<PathNode, Map> remaining = graph.get(node);
+            if (remaining != null) {
+                final PathNode key = findKey(node);
+                return new LinkFollower(remaining, Mode.FOLLOWING, key);
+            } else {
+                return terminated(node);
+            }
+        }
+        return terminated(node);
+    }
+
+    /**
+     * somewhat bizarre, but we have to find the actual node that is in the
+     * graph; the one we matching on doesn't match on the
+     * {@link PathNode#getCriteria()} map.
+     */
+    private PathNode findKey(final PathNode node) {
+        final Set<PathNode> keySet = graph.keySet();
+        for (final PathNode key : keySet) {
+            if (key.equals(node)) {
+                return key;
+            }
+        }
+        // shouldn't happen
+        return node;
+    }
+
+    private static LinkFollower terminated(final PathNode node) {
+        return new LinkFollower(null, Mode.TERMINATED, node);
+    }
+
+    /**
+     * Not public API; use {@link #matches(JsonRepresentation)}.
+     */
+    boolean isFollowing() {
+        return mode == Mode.FOLLOWING;
+    }
+
+    public boolean isTerminated() {
+        return mode == Mode.TERMINATED;
+    }
+
+    public Map<String, String> criteria() {
+        return Collections.unmodifiableMap(root.getCriteria());
+    }
+
+    /**
+     * Ensure that every key present in the provided map matches the criterium.
+     * 
+     * <p>
+     * Any keys in the criterium that are not present in the map will be
+     * ignored.
+     */
+    public boolean matches(final JsonRepresentation link) {
+        if (!isFollowing()) {
+            return false;
+        }
+        return root == null || root.matches(link);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/NOTUSED_WellKnownType.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/NOTUSED_WellKnownType.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/NOTUSED_WellKnownType.java
new file mode 100644
index 0000000..131c265
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/NOTUSED_WellKnownType.java
@@ -0,0 +1,80 @@
+/*
+ *  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;
+
+// TODO: remove, not used?
+enum NOTUSED_WellKnownType {
+
+    STRING(java.lang.String.class), 
+    BYTE(java.lang.Byte.class), 
+    SHORT(java.lang.Short.class), 
+    INT(java.lang.Integer.class), 
+    LONG(java.lang.Long.class), 
+    BOOLEAN(java.lang.Boolean.class), 
+    FLOAT(java.lang.Float.class), 
+    DOUBLE(java.lang.Double.class), 
+    BIGINT(java.math.BigInteger.class), 
+    BIGDEC(java.math.BigDecimal.class), 
+    OBJECT(java.lang.Object.class), 
+    LIST(java.util.List.class), 
+    SET(java.util.Set.class);
+
+    private final Class<?> cls;
+    private final String className;
+
+    private NOTUSED_WellKnownType(final Class<?> cls) {
+        this.cls = cls;
+        this.className = cls.getName();
+    }
+
+    private String getName() {
+        return name().toLowerCase();
+    }
+
+    private static NOTUSED_WellKnownType lookup(final Class<?> cls) {
+        for (final NOTUSED_WellKnownType wellKnownType : values()) {
+            if (wellKnownType.cls.equals(cls)) {
+                return wellKnownType;
+            }
+        }
+        return null;
+    }
+
+    private  static NOTUSED_WellKnownType lookup(final String className) {
+        for (final NOTUSED_WellKnownType wellKnownType : values()) {
+            if (wellKnownType.className.equals(className)) {
+                return wellKnownType;
+            }
+        }
+        return null;
+    }
+
+    @SuppressWarnings("unused")
+    private  static String canonical(final String className) {
+        final NOTUSED_WellKnownType wellKnownType = NOTUSED_WellKnownType.lookup(className);
+        return wellKnownType != null ? wellKnownType.getName() : className;
+    }
+
+    @SuppressWarnings("unused")
+    private  static String canonical(final Class<?> cls) {
+        final NOTUSED_WellKnownType wellKnownType = NOTUSED_WellKnownType.lookup(cls);
+        return wellKnownType != null ? wellKnownType.getName() : cls.getName();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/RendererContext.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/RendererContext.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/RendererContext.java
new file mode 100644
index 0000000..22d702d
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/RendererContext.java
@@ -0,0 +1,23 @@
+package org.apache.isis.viewer.restfulobjects.rendering;
+
+import java.util.List;
+
+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.metamodel.adapter.mgr.AdapterManager;
+
+public interface RendererContext {
+
+    public String urlFor(final String url);
+
+    public AuthenticationSession getAuthenticationSession();
+    
+    public AdapterManager getAdapterManager();
+
+    public List<List<String>> getFollowLinks();
+    
+    public Where getWhere();
+    
+    public Localization getLocalization();
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/RendererFactory.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/RendererFactory.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/RendererFactory.java
new file mode 100644
index 0000000..d05d712
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/RendererFactory.java
@@ -0,0 +1,30 @@
+/*
+ *  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;
+
+import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
+import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
+
+public interface RendererFactory {
+
+    RepresentationType getRepresentationType();
+
+    ReprRenderer<?, ?> newRenderer(RendererContext resourceContext, LinkFollower linkFollower, JsonRepresentation representation);
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/RendererFactoryRegistry.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/RendererFactoryRegistry.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/RendererFactoryRegistry.java
new file mode 100644
index 0000000..7799e52
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/RendererFactoryRegistry.java
@@ -0,0 +1,83 @@
+/*
+ *  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;
+
+import java.util.Map;
+
+import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
+import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ActionResultReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.DomainObjectReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ListReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectActionReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectCollectionReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectPropertyReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ScalarValueReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.ActionDescriptionReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.ActionParameterDescriptionReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.CollectionDescriptionReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.DomainTypeReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.PropertyDescriptionReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.TypeActionResultReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.TypeListReprRenderer;
+
+import com.google.common.collect.Maps;
+import com.google.common.net.MediaType;
+
+public class RendererFactoryRegistry {
+
+    // TODO: get rid of this horrible singleton
+    public final static RendererFactoryRegistry instance = new RendererFactoryRegistry();
+
+    private final Map<MediaType, RendererFactory> factoryByReprType = Maps.newHashMap();
+
+    RendererFactoryRegistry() {
+        registerDefaults();
+    }
+
+    private void registerDefaults() {
+        register(new DomainObjectReprRenderer.Factory());
+        register(new ObjectPropertyReprRenderer.Factory());
+        register(new ObjectCollectionReprRenderer.Factory());
+        register(new ObjectActionReprRenderer.Factory());
+        register(new ActionResultReprRenderer.Factory());
+        register(new ListReprRenderer.Factory());
+        register(new ScalarValueReprRenderer.Factory());
+        register(new TypeListReprRenderer.Factory());
+        register(new DomainTypeReprRenderer.Factory());
+        register(new PropertyDescriptionReprRenderer.Factory());
+        register(new CollectionDescriptionReprRenderer.Factory());
+        register(new ActionDescriptionReprRenderer.Factory());
+        register(new ActionParameterDescriptionReprRenderer.Factory());
+        register(new TypeActionResultReprRenderer.Factory());
+    }
+
+    public void register(final RendererFactory factory) {
+        final RepresentationType representationType = factory.getRepresentationType();
+        factoryByReprType.put(representationType.getMediaType(), factory);
+    }
+
+    public RendererFactory find(final MediaType mediaType) {
+        return factoryByReprType.get(mediaType);
+    }
+
+    public RendererFactory find(final RepresentationType representationType) {
+        return find(representationType.getMediaType());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRenderer.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRenderer.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRenderer.java
new file mode 100644
index 0000000..81a03da
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRenderer.java
@@ -0,0 +1,32 @@
+/*
+ *  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;
+
+import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
+import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
+
+public interface ReprRenderer<R extends ReprRenderer<R, T>, T> {
+
+    RepresentationType getRepresentationType();
+
+    R with(T t);
+
+    public JsonRepresentation render();
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRendererAbstract.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRendererAbstract.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRendererAbstract.java
new file mode 100644
index 0000000..0a68617
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRendererAbstract.java
@@ -0,0 +1,173 @@
+/*
+ *  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;
+
+import java.util.List;
+
+import org.apache.isis.core.commons.authentication.AuthenticationSession;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.runtime.system.transaction.UpdateNotifier;
+import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
+import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
+import org.apache.isis.viewer.restfulobjects.applib.links.Rel;
+import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.DomainObjectReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.DomainTypeReprRenderer;
+
+public abstract class ReprRendererAbstract<R extends ReprRendererAbstract<R, T>, T> implements ReprRenderer<R, T> {
+
+    protected final RendererContext resourceContext;
+    private final LinkFollower linkFollower;
+    private final RepresentationType representationType;
+    protected final JsonRepresentation representation;
+
+    protected boolean includesSelf;
+
+    public ReprRendererAbstract(final RendererContext resourceContext, final LinkFollower linkFollower, final RepresentationType representationType, final JsonRepresentation representation) {
+        this.resourceContext = resourceContext;
+        this.linkFollower = asProvidedElseCreate(linkFollower);
+        this.representationType = representationType;
+        this.representation = representation;
+    }
+
+    public RendererContext getResourceContext() {
+        return resourceContext;
+    }
+
+    public LinkFollower getLinkFollower() {
+        return linkFollower;
+    }
+
+    private LinkFollower asProvidedElseCreate(final LinkFollower linkFollower) {
+        if (linkFollower != null) {
+            return linkFollower;
+        }
+        return LinkFollower.create(resourceContext.getFollowLinks());
+    }
+
+    @Override
+    public RepresentationType getRepresentationType() {
+        return representationType;
+    }
+
+    @SuppressWarnings("unchecked")
+    public R includesSelf() {
+        this.includesSelf = true;
+        return (R) this;
+    }
+
+    public R withSelf(final String href) {
+        if (href != null) {
+            getLinks().arrayAdd(LinkBuilder.newBuilder(resourceContext, Rel.SELF, representationType, href).build());
+        }
+        return cast(this);
+    }
+
+    public R withSelf(final JsonRepresentation link) {
+        final String rel = link.getString("rel");
+        if (rel == null || !rel.equals(Rel.SELF.getName())) {
+            throw new IllegalArgumentException("Provided link does not have a 'rel' of 'self'; was: " + link);
+        }
+        if (link != null) {
+            getLinks().arrayAdd(link);
+        }
+        return cast(this);
+    }
+
+    /**
+     * Will lazily create links array as required
+     */
+    protected JsonRepresentation getLinks() {
+        JsonRepresentation links = representation.getArray("links");
+        if (links == null) {
+            links = JsonRepresentation.newArray();
+            representation.mapPut("links", links);
+        }
+        return links;
+    }
+
+    protected void addLink(final Rel rel, final ObjectSpecification objectSpec) {
+        if (objectSpec == null) {
+            return;
+        }
+        final LinkBuilder linkBuilder = DomainTypeReprRenderer.newLinkToBuilder(getResourceContext(), rel, objectSpec);
+        getLinks().arrayAdd(linkBuilder.build());
+    }
+
+    /**
+     * Will lazily create extensions map as required
+     */
+    protected JsonRepresentation getExtensions() {
+        JsonRepresentation extensions = representation.getMap("extensions");
+        if (extensions == null) {
+            extensions = JsonRepresentation.newMap();
+            representation.mapPut("extensions", extensions);
+        }
+        return extensions;
+    }
+
+    public R withExtensions(final JsonRepresentation extensions) {
+        if (!extensions.isMap()) {
+            throw new IllegalArgumentException("extensions must be a map");
+        }
+        representation.mapPut("extensions", extensions);
+        return cast(this);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected static <R extends ReprRendererAbstract<R, T>, T> R cast(final ReprRendererAbstract<R, T> builder) {
+        return (R) builder;
+    }
+
+    @Override
+    public abstract JsonRepresentation render();
+
+    /**
+     * Convenience for representations that are returned from objects that
+     * mutate state.
+     */
+    protected final void addExtensionsIsisProprietaryChangedObjects() {
+        final UpdateNotifier updateNotifier = getUpdateNotifier();
+
+        addToExtensions("changed", updateNotifier.getChangedObjects());
+        addToExtensions("disposed", updateNotifier.getDisposedObjects());
+    }
+
+    private void addToExtensions(final String key, final List<ObjectAdapter> adapters) {
+        final JsonRepresentation adapterList = JsonRepresentation.newArray();
+        getExtensions().mapPut(key, adapterList);
+        for (final ObjectAdapter adapter : adapters) {
+            adapterList.arrayAdd(DomainObjectReprRenderer.newLinkToBuilder(getResourceContext(), Rel.OBJECT, adapter).build());
+        }
+    }
+
+    protected List<ObjectAdapter> getServiceAdapters() {
+        return IsisContext.getPersistenceSession().getServices();
+    }
+
+    protected AuthenticationSession getAuthenticationSession() {
+        return IsisContext.getAuthenticationSession();
+    }
+
+    protected UpdateNotifier getUpdateNotifier() {
+        return IsisContext.getCurrentTransaction().getUpdateNotifier();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRendererException.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRendererException.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRendererException.java
new file mode 100644
index 0000000..b8cbfca
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRendererException.java
@@ -0,0 +1,57 @@
+/*
+ *  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;
+
+import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
+
+public class ReprRendererException extends RuntimeException {
+
+    public static ReprRendererException create(final String message, final Object... args) {
+        return create((Exception) null, message, args);
+    }
+
+    public static ReprRendererException create(final Exception cause) {
+        return create(cause, null);
+    }
+
+    public static ReprRendererException create(final Exception cause, final String message, final Object... args) {
+        return new ReprRendererException(formatString(message, args), cause, null);
+    }
+
+    public static ReprRendererException create(final JsonRepresentation repr, final String message, final Object... args) {
+        return new ReprRendererException(formatString(message, args), null, repr);
+    }
+
+    private static String formatString(final String formatStr, final Object... args) {
+        return formatStr != null ? String.format(formatStr, args) : null;
+    }
+
+    private static final long serialVersionUID = 1L;
+    private final JsonRepresentation jsonRepresentation;
+
+    private ReprRendererException(final String message, final Throwable ex, final JsonRepresentation jsonRepresentation) {
+        super(message, ex);
+        this.jsonRepresentation = jsonRepresentation;
+    }
+
+    public JsonRepresentation getJsonRepresentation() {
+        return jsonRepresentation;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRendererFactoryAbstract.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRendererFactoryAbstract.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRendererFactoryAbstract.java
new file mode 100644
index 0000000..69477db
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/ReprRendererFactoryAbstract.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.viewer.restfulobjects.rendering;
+
+import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
+
+public abstract class ReprRendererFactoryAbstract implements RendererFactory {
+
+    private final RepresentationType representationType;
+
+    public ReprRendererFactoryAbstract(final RepresentationType representationType) {
+        this.representationType = representationType;
+    }
+
+    @Override
+    public RepresentationType getRepresentationType() {
+        return representationType;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/AbstractObjectMemberReprRenderer.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/AbstractObjectMemberReprRenderer.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/AbstractObjectMemberReprRenderer.java
new file mode 100644
index 0000000..73c91b9
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/AbstractObjectMemberReprRenderer.java
@@ -0,0 +1,257 @@
+/**
+ *  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.domainobjects;
+
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
+import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
+import org.apache.isis.viewer.restfulobjects.applib.links.Rel;
+import org.apache.isis.viewer.restfulobjects.rendering.LinkFollower;
+import org.apache.isis.viewer.restfulobjects.rendering.RendererContext;
+import org.apache.isis.viewer.restfulobjects.rendering.ReprRendererAbstract;
+import org.codehaus.jackson.node.NullNode;
+
+public abstract class AbstractObjectMemberReprRenderer<R extends ReprRendererAbstract<R, ObjectAndMember<T>>, T extends ObjectMember> extends ReprRendererAbstract<R, ObjectAndMember<T>> {
+
+    protected enum Mode {
+        INLINE, FOLLOWED, STANDALONE, MUTATED, ARGUMENTS;
+
+        public boolean isInline() {
+            return this == INLINE;
+        }
+
+        public boolean isFollowed() {
+            return this == FOLLOWED;
+        }
+
+        public boolean isStandalone() {
+            return this == STANDALONE;
+        }
+
+        public boolean isMutated() {
+            return this == MUTATED;
+        }
+
+        public boolean isArguments() {
+            return this == ARGUMENTS;
+        }
+    }
+
+    protected ObjectAdapterLinkTo linkTo;
+
+    protected ObjectAdapter objectAdapter;
+    protected MemberType memberType;
+    protected T objectMember;
+    protected Mode mode = Mode.INLINE; // unless we determine otherwise
+
+    private final Where where;
+
+    public AbstractObjectMemberReprRenderer(final RendererContext resourceContext, final LinkFollower linkFollower, final RepresentationType representationType, final JsonRepresentation representation, Where where) {
+        super(resourceContext, linkFollower, representationType, representation);
+        this.where = where;
+    }
+
+    @Override
+    public R with(final ObjectAndMember<T> objectAndMember) {
+        this.objectAdapter = objectAndMember.getObjectAdapter();
+        this.objectMember = objectAndMember.getMember();
+        this.memberType = MemberType.determineFrom(objectMember);
+        usingLinkTo(new DomainObjectLinkTo());
+
+        // done eagerly so can use as criteria for x-ro-follow-links
+        representation.mapPut(memberType.getJsProp(), objectMember.getId());
+        representation.mapPut("memberType", memberType.getName());
+
+        return cast(this);
+    }
+
+    /**
+     * Must be called after {@link #with(ObjectAndMember)} (which provides the
+     * {@link #objectAdapter}).
+     */
+    public R usingLinkTo(final ObjectAdapterLinkTo linkTo) {
+        this.linkTo = linkTo.usingUrlBase(resourceContext).with(objectAdapter);
+        return cast(this);
+    }
+
+    /**
+     * Indicate that this is a standalone representation.
+     */
+    public R asStandalone() {
+        mode = Mode.STANDALONE;
+        return cast(this);
+    }
+
+    /**
+     * Indicate that this is a representation to include as the result of a
+     * followed link.
+     */
+    public R asFollowed() {
+        mode = Mode.FOLLOWED;
+        return cast(this);
+    }
+
+    /**
+     * Indicates that the representation was produced as the result of a
+     * resource that mutated the state.
+     * 
+     * <p>
+     * The effect of this is to suppress the link to self.
+     */
+    public R asMutated() {
+        mode = Mode.MUTATED;
+        return cast(this);
+    }
+
+    public R asArguments() {
+        mode = Mode.ARGUMENTS;
+        return cast(this);
+    }
+
+    /**
+     * For subclasses to call from their {@link #render()} method.
+     */
+    protected void renderMemberContent() {
+        if (mode.isInline()) {
+            addDetailsLinkIfPersistent();
+        }
+
+        if (mode.isStandalone()) {
+            addLinkToSelf();
+        }
+
+        if (mode.isStandalone() || mode.isMutated()) {
+            addLinkToUp();
+        }
+
+        if (mode.isFollowed() || mode.isStandalone() || mode.isMutated()) {
+            addMutatorsIfEnabled();
+
+            putExtensionsIsisProprietary();
+            addLinksToFormalDomainModel();
+            addLinksIsisProprietary();
+        }
+    }
+
+    private void addLinkToSelf() {
+        getLinks().arrayAdd(linkTo.memberBuilder(Rel.SELF, memberType, objectMember).build());
+    }
+
+    private void addLinkToUp() {
+        getLinks().arrayAdd(linkTo.builder(Rel.UP).build());
+    }
+
+    protected abstract void addMutatorsIfEnabled();
+
+    /**
+     * For subclasses to call back to when {@link #addMutatorsIfEnabled() adding
+     * mutators}.
+     */
+    protected void addLinkFor(final MutatorSpec mutatorSpec) {
+        if (!hasMemberFacet(mutatorSpec.mutatorFacetType)) {
+            return;
+        }
+        final JsonRepresentation arguments = mutatorArgs(mutatorSpec);
+        final RepresentationType representationType = memberType.getRepresentationType();
+        final JsonRepresentation mutatorLink = linkToForMutatorInvoke().memberBuilder(mutatorSpec.rel, memberType, objectMember, representationType, mutatorSpec.suffix).withHttpMethod(mutatorSpec.httpMethod).withArguments(arguments).build();
+        getLinks().arrayAdd(mutatorLink);
+    }
+
+    /**
+     * Hook to allow actions to render invoke links that point to the
+     * contributing service.
+     */
+    protected ObjectAdapterLinkTo linkToForMutatorInvoke() {
+        return linkTo;
+    }
+
+    /**
+     * Default implementation (common to properties and collections) that can be
+     * overridden (ie by actions) if required.
+     */
+    protected JsonRepresentation mutatorArgs(final MutatorSpec mutatorSpec) {
+        if (mutatorSpec.arguments.isNone()) {
+            return null;
+        }
+        if (mutatorSpec.arguments.isOne()) {
+            final JsonRepresentation repr = JsonRepresentation.newMap();
+            repr.mapPut("value", NullNode.getInstance()); // force a null into
+                                                          // the map
+            return repr;
+        }
+        // overridden by actions
+        throw new UnsupportedOperationException("override mutatorArgs() to populate for many arguments");
+    }
+
+    private void addDetailsLinkIfPersistent() {
+        if (!objectAdapter.representsPersistent()) {
+            return;
+        }
+        final JsonRepresentation link = linkTo.memberBuilder(Rel.DETAILS, memberType, objectMember).build();
+        getLinks().arrayAdd(link);
+
+        final LinkFollower membersLinkFollower = getLinkFollower();
+        final LinkFollower detailsLinkFollower = membersLinkFollower.follow("links[rel=%s]", Rel.DETAILS.getName());
+        if (membersLinkFollower.matches(representation) && detailsLinkFollower.matches(link)) {
+            followDetailsLink(link);
+        }
+        return;
+    }
+
+    protected abstract void followDetailsLink(JsonRepresentation detailsLink);
+
+    protected final void putDisabledReasonIfDisabled() {
+        final String disabledReasonRep = usability().getReason();
+        representation.mapPut("disabledReason", disabledReasonRep);
+    }
+
+    protected abstract void putExtensionsIsisProprietary();
+
+    protected abstract void addLinksToFormalDomainModel();
+
+    protected abstract void addLinksIsisProprietary();
+
+    /**
+     * Convenience method.
+     */
+    public boolean isMemberVisible() {
+        return visibility().isAllowed();
+    }
+
+    protected <F extends Facet> F getMemberSpecFacet(final Class<F> facetType) {
+        final ObjectSpecification otoaSpec = objectMember.getSpecification();
+        return otoaSpec.getFacet(facetType);
+    }
+
+    protected boolean hasMemberFacet(final Class<? extends Facet> facetType) {
+        return objectMember.getFacet(facetType) != null;
+    }
+
+    protected Consent usability() {
+        return objectMember.isUsable(getAuthenticationSession(), objectAdapter, where);
+    }
+
+    protected Consent visibility() {
+        return objectMember.isVisible(getAuthenticationSession(), objectAdapter, where);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ActionResultReprRenderer.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ActionResultReprRenderer.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ActionResultReprRenderer.java
new file mode 100644
index 0000000..5109cb5
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ActionResultReprRenderer.java
@@ -0,0 +1,183 @@
+/**
+ *  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.domainobjects;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
+import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
+import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
+import org.apache.isis.viewer.restfulobjects.applib.domainobjects.ActionResultRepresentation.ResultType;
+import org.apache.isis.viewer.restfulobjects.applib.links.Rel;
+import org.apache.isis.viewer.restfulobjects.rendering.LinkBuilder;
+import org.apache.isis.viewer.restfulobjects.rendering.LinkFollower;
+import org.apache.isis.viewer.restfulobjects.rendering.RendererContext;
+import org.apache.isis.viewer.restfulobjects.rendering.RendererFactory;
+import org.apache.isis.viewer.restfulobjects.rendering.RendererFactoryRegistry;
+import org.apache.isis.viewer.restfulobjects.rendering.ReprRenderer;
+import org.apache.isis.viewer.restfulobjects.rendering.ReprRendererAbstract;
+import org.apache.isis.viewer.restfulobjects.rendering.ReprRendererFactoryAbstract;
+
+public class ActionResultReprRenderer extends ReprRendererAbstract<ActionResultReprRenderer, ObjectAndActionInvocation> {
+
+    private ObjectAdapterLinkTo adapterLinkTo = new DomainObjectLinkTo();
+
+    private ObjectAdapter objectAdapter;
+    private ObjectAction action;
+    private JsonRepresentation arguments;
+    private ObjectAdapter returnedAdapter;
+
+    public static class Factory extends ReprRendererFactoryAbstract {
+
+        public Factory() {
+            super(RepresentationType.ACTION_RESULT);
+        }
+
+        @Override
+        public ReprRenderer<?, ?> newRenderer(final RendererContext resourceContext, final LinkFollower linkFollower, final JsonRepresentation representation) {
+            return new ActionResultReprRenderer(resourceContext, linkFollower, getRepresentationType(), representation);
+        }
+    }
+
+    private ActionResultReprRenderer(final RendererContext resourceContext, final LinkFollower linkFollower, final RepresentationType representationType, final JsonRepresentation representation) {
+        super(resourceContext, linkFollower, representationType, representation);
+    }
+
+    @Override
+    public ActionResultReprRenderer with(final ObjectAndActionInvocation objectAndActionInvocation) {
+
+        objectAdapter = objectAndActionInvocation.getObjectAdapter();
+        action = objectAndActionInvocation.getAction();
+        arguments = objectAndActionInvocation.getArguments();
+        returnedAdapter = objectAndActionInvocation.getReturnedAdapter();
+
+        adapterLinkTo.with(returnedAdapter);
+
+        return this;
+    }
+
+    public void using(final ObjectAdapterLinkTo adapterLinkTo) {
+        this.adapterLinkTo = adapterLinkTo.with(objectAdapter);
+    }
+
+    @Override
+    public JsonRepresentation render() {
+
+        representationWithSelfFor(action, arguments);
+
+        addResult(representation);
+
+        addExtensionsIsisProprietaryChangedObjects();
+
+        return representation;
+    }
+
+    private void addResult(final JsonRepresentation representation) {
+        final JsonRepresentation result = JsonRepresentation.newMap();
+        final ResultType resultType = addResultTo(result);
+
+        if (!resultType.isVoid()) {
+            putResultType(representation, resultType);
+            representation.mapPut("result", result);
+        }
+    }
+
+    private ResultType addResultTo(final JsonRepresentation result) {
+
+        final ObjectSpecification returnType = this.action.getReturnType();
+
+        if (returnType.getCorrespondingClass() == void.class) {
+            // void
+            return ResultType.VOID;
+        }
+
+        final CollectionFacet collectionFacet = returnType.getFacet(CollectionFacet.class);
+        if (collectionFacet != null) {
+            // collection
+
+            final Collection<ObjectAdapter> collectionAdapters = collectionFacet.collection(returnedAdapter);
+
+            final RendererFactory factory = getRendererFactoryRegistry().find(RepresentationType.LIST);
+            final ListReprRenderer renderer = (ListReprRenderer) factory.newRenderer(resourceContext, null, result);
+            renderer.with(collectionAdapters).withReturnType(action.getReturnType()).withElementType(returnedAdapter.getElementSpecification());
+
+            renderer.render();
+            return ResultType.LIST;
+        }
+
+        final EncodableFacet encodableFacet = returnType.getFacet(EncodableFacet.class);
+        if (encodableFacet != null) {
+            // scalar
+
+            final RendererFactory factory = getRendererFactoryRegistry().find(RepresentationType.SCALAR_VALUE);
+
+            final ScalarValueReprRenderer renderer = (ScalarValueReprRenderer) factory.newRenderer(resourceContext, null, result);
+            renderer.with(returnedAdapter).withReturnType(action.getReturnType());
+
+            renderer.render();
+            return ResultType.SCALAR_VALUE;
+
+        }
+
+        {
+            // object
+            final RendererFactory factory = getRendererFactoryRegistry().find(RepresentationType.DOMAIN_OBJECT);
+            final DomainObjectReprRenderer renderer = (DomainObjectReprRenderer) factory.newRenderer(resourceContext, null, result);
+
+            renderer.with(returnedAdapter).includesSelf();
+
+            renderer.render();
+            return ResultType.DOMAIN_OBJECT;
+        }
+    }
+
+    private void putResultType(final JsonRepresentation representation, final ResultType resultType) {
+        representation.mapPut("resulttype", resultType.getValue());
+    }
+
+    private JsonRepresentation representationWithSelfFor(final ObjectAction action, final JsonRepresentation bodyArgs) {
+        final JsonRepresentation links = JsonRepresentation.newArray();
+        representation.mapPut("links", links);
+
+        final LinkBuilder selfLinkBuilder = adapterLinkTo.memberBuilder(Rel.SELF, MemberType.ACTION, action, RepresentationType.ACTION_RESULT, "invoke");
+
+        // TODO: remove duplication with AbstractObjectMember#addLinkTo
+        final MemberType memberType = MemberType.of(action);
+        final Map<String, MutatorSpec> mutators = memberType.getMutators();
+
+        final String mutator = InvokeKeys.getKeyFor(action.getSemantics());
+        final MutatorSpec mutatorSpec = mutators.get(mutator);
+        selfLinkBuilder.withHttpMethod(mutatorSpec.httpMethod);
+
+        final JsonRepresentation selfLink = selfLinkBuilder.build();
+
+        links.arrayAdd(selfLink);
+        selfLink.mapPut("args", bodyArgs);
+        return representation;
+    }
+
+    protected RendererFactoryRegistry getRendererFactoryRegistry() {
+        // TODO: yuck
+        return RendererFactoryRegistry.instance;
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/BodyArgs.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/BodyArgs.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/BodyArgs.java
new file mode 100644
index 0000000..2fc35b9
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/BodyArgs.java
@@ -0,0 +1,41 @@
+/**
+ *  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.domainobjects;
+
+public enum BodyArgs {
+    /**
+     * eg property clear
+     */
+    NONE,
+    /**
+     * eg property modify, collection addto/remove
+     */
+    ONE,
+    /**
+     * eg action invoke
+     */
+    MANY;
+
+    public boolean isOne() {
+        return this == ONE;
+    }
+
+    public boolean isNone() {
+        return this == NONE;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/CollectionSemantics.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/CollectionSemantics.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/CollectionSemantics.java
new file mode 100644
index 0000000..4075016
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/CollectionSemantics.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.viewer.restfulobjects.rendering.domainobjects;
+
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+
+public enum CollectionSemantics {
+
+    SET("addToSet"), 
+    LIST("addToList");
+
+    private final String addToKey;
+
+    private CollectionSemantics(final String addToKey) {
+        this.addToKey = addToKey;
+    }
+
+    public String getAddToKey() {
+        return addToKey;
+    }
+
+    public String getRemoveFromKey() {
+        return "removeFrom";
+    }
+
+    public static CollectionSemantics determine(final OneToManyAssociation collection) {
+        return collection.getCollectionSemantics().isSet() ? CollectionSemantics.SET : CollectionSemantics.LIST;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/bb79d33e/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/DomainObjectLinkTo.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/DomainObjectLinkTo.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/DomainObjectLinkTo.java
new file mode 100644
index 0000000..03a78c8
--- /dev/null
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/DomainObjectLinkTo.java
@@ -0,0 +1,113 @@
+/**
+ *  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.domainobjects;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
+import org.apache.isis.viewer.restfulobjects.applib.links.Rel;
+import org.apache.isis.viewer.restfulobjects.rendering.LinkBuilder;
+import org.apache.isis.viewer.restfulobjects.rendering.RendererContext;
+
+public class DomainObjectLinkTo implements ObjectAdapterLinkTo {
+
+    protected RendererContext resourceContext;
+    protected ObjectAdapter objectAdapter;
+
+    @Override
+    public final DomainObjectLinkTo usingUrlBase(final RendererContext resourceContext) {
+        this.resourceContext = resourceContext;
+        return this;
+    }
+
+    @Override
+    public ObjectAdapterLinkTo with(final ObjectAdapter objectAdapter) {
+        this.objectAdapter = objectAdapter;
+        return this;
+    }
+
+    @Override
+    public LinkBuilder builder() {
+        return builder(null);
+    }
+
+    @Override
+    public LinkBuilder builder(final Rel rel) {
+        final LinkBuilder linkBuilder = LinkBuilder.newBuilder(resourceContext, relElseDefault(rel), RepresentationType.DOMAIN_OBJECT, linkRef());
+        linkBuilder.withTitle(objectAdapter.titleString());
+        return linkBuilder;
+    }
+
+    /**
+     * hook method
+     */
+    protected String linkRef() {
+        if (resourceContext == null) {
+            throw new IllegalStateException("resourceContext not provided");
+        }
+        if (objectAdapter == null) {
+            throw new IllegalStateException("objectAdapter not provided");
+        }
+        final StringBuilder buf = new StringBuilder("objects/");
+        buf.append(objectAdapter.getOid().enString(getOidMarshaller()));
+        return buf.toString();
+    }
+
+    private Rel relElseDefault(final Rel rel) {
+        return rel != null ? rel : defaultRel();
+    }
+
+    /**
+     * hook method; used by {@link #builder(Rel)}.
+     */
+    protected Rel defaultRel() {
+        return Rel.OBJECT;
+    }
+
+    @Override
+    public final LinkBuilder memberBuilder(final Rel rel, final MemberType memberType, final ObjectMember objectMember, final String... parts) {
+        return memberBuilder(rel, memberType, objectMember, memberType.getRepresentationType(), parts);
+    }
+
+    @Override
+    public final LinkBuilder memberBuilder(final Rel rel, final MemberType memberType, final ObjectMember objectMember, final RepresentationType representationType, final String... parts) {
+        final StringBuilder buf = new StringBuilder(linkRef());
+        buf.append("/").append(memberType.getUrlPart()).append(objectMember.getId());
+        for (final String part : parts) {
+            if (part == null) {
+                continue;
+            }
+            buf.append("/").append(part);
+        }
+        final String url = buf.toString();
+        return LinkBuilder.newBuilder(resourceContext, rel, representationType, url);
+    }
+
+
+    
+    //////////////////////////////////////////////////
+    // Dependencies (from context)
+    //////////////////////////////////////////////////
+    
+    protected static OidMarshaller getOidMarshaller() {
+        return IsisContext.getOidMarshaller();
+    }
+
+
+}
\ No newline at end of file