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/03/13 00:32:21 UTC

[38/38] git commit: ISIS-233: argument format and validation

Updated Branches:
  refs/heads/dan/ISIS-233-ro ae68e14cd -> 0ec704b6d (forced update)


ISIS-233: argument format and validation

* the argument processing was missing the 'value' node
* now rendering the body with 'invalidReason' if validation error occurs.


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

Branch: refs/heads/dan/ISIS-233-ro
Commit: 0ec704b6dab6d418ca26761a6869e5c49ee8f527
Parents: 09b1a7a
Author: Dan Haywood <da...@apache.org>
Authored: Tue Mar 12 23:30:42 2013 +0000
Committer: Dan Haywood <da...@apache.org>
Committed: Tue Mar 12 23:30:42 2013 +0000

----------------------------------------------------------------------
 .../applib/client/RestfulResponse.java             |    5 +
 .../rendering/domainobjects/JsonValueEncoder.java  |  126 ++++++++-------
 .../domainobjects/ObjectActionReprRenderer.java    |    2 +-
 .../restfulobjects/server/ResourceContext.java     |    2 +-
 .../server/RestfulObjectsApplicationException.java |   28 ++--
 .../RestfulObjectsApplicationExceptionMapper.java  |   28 +++-
 .../resources/DomainObjectResourceServerside.java  |   32 ++--
 .../server/resources/DomainResourceHelper.java     |   79 ++++++----
 .../resources/DomainTypeResourceServerside.java    |    2 +-
 .../server/resources/ResourceAbstract.java         |    4 +-
 ...stfulObjectsApplicationExceptionMapperTest.java |    4 +-
 .../DomainServiceTest_req_safe_refarg_bad.java     |   32 ++---
 ...ainServiceTest_req_safe_refarg_resp_scalar.java |   33 ++---
 ...inServiceTest_req_safe_simplearg_resp_list.java |    8 +-
 14 files changed, 208 insertions(+), 177 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/applib/src/main/java/org/apache/isis/viewer/restfulobjects/applib/client/RestfulResponse.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/applib/src/main/java/org/apache/isis/viewer/restfulobjects/applib/client/RestfulResponse.java b/component/viewer/restfulobjects/applib/src/main/java/org/apache/isis/viewer/restfulobjects/applib/client/RestfulResponse.java
index 6357be1..758179c 100644
--- a/component/viewer/restfulobjects/applib/src/main/java/org/apache/isis/viewer/restfulobjects/applib/client/RestfulResponse.java
+++ b/component/viewer/restfulobjects/applib/src/main/java/org/apache/isis/viewer/restfulobjects/applib/client/RestfulResponse.java
@@ -269,6 +269,10 @@ public class RestfulResponse<T> {
             return parser.valueOf(value);
         }
 
+        public String render(X message) {
+            return parser.asString(message);
+        }
+
         private static Parser<String> warningParser() {
             return new Parser<String>(){
                 private static final String PREFIX = "199 RestfulObjects ";
@@ -287,6 +291,7 @@ public class RestfulResponse<T> {
                 }
             };
         }
+
     }
 
     private final Response response;

http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueEncoder.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueEncoder.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueEncoder.java
index 957aa67..6628dfc 100644
--- a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueEncoder.java
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueEncoder.java
@@ -36,134 +36,146 @@ public final class JsonValueEncoder {
         private static final long serialVersionUID = 1L;
     }
 
-    public ObjectAdapter asAdapter(final ObjectSpecification objectSpec, final JsonRepresentation representation) {
+    public ObjectAdapter asAdapter(final ObjectSpecification objectSpec, final JsonRepresentation argRepr) {
         if (objectSpec == null) {
-            throw new IllegalArgumentException("objectSpec cannot be null");
+            String reason = "ObjectSpec is null, cannot validate";
+            argRepr.mapPut("invalidReason", reason);
+            throw new IllegalArgumentException(reason);
         }
         final EncodableFacet encodableFacet = objectSpec.getFacet(EncodableFacet.class);
         if (encodableFacet == null) {
-            throw new IllegalArgumentException("objectSpec expected to have EncodableFacet");
+            String reason = "ObjectSpec expected to have an EncodableFacet";
+            argRepr.mapPut("invalidReason", reason);
+            throw new IllegalArgumentException(reason);
         }
-        if (representation == null) {
-            throw new IllegalArgumentException("representation cannot be null");
+        final JsonRepresentation argValueRepr = argRepr.getRepresentation("value");
+        if(argValueRepr == null) {
+            String reason = "No 'value' key";
+            argRepr.mapPut("invalidReason", reason);
+            throw new IllegalArgumentException(reason);
         }
-        if (!representation.isValue()) {
-            throw new IllegalArgumentException("representation must be of a value");
+        if (!argValueRepr.isValue()) {
+            String reason = "Representation must be of a value";
+            argRepr.mapPut("invalidReason", reason);
+            throw new IllegalArgumentException(reason);
         }
 
         // special case handling for JSON built-ins
         if (isBoolean(objectSpec)) {
-            if (!representation.isBoolean()) {
-                throwIncompatibleException(objectSpec, representation);
+            if (!argValueRepr.isBoolean()) {
+                throwIncompatibleException(objectSpec, argRepr);
             }
-            final String argStr = "" + representation.asBoolean();
+            final String argStr = "" + argValueRepr.asBoolean();
             return encodableFacet.fromEncodedString(argStr);
         }
 
         if (isInteger(objectSpec)) {
-            if (representation.isInt()) {
-                final String argStr = "" + representation.asInt();
+            if (argValueRepr.isInt()) {
+                final String argStr = "" + argValueRepr.asInt();
                 return encodableFacet.fromEncodedString(argStr);
             }
             // best effort
-            if (representation.isString()) {
-                final String argStr = representation.asString();
+            if (argValueRepr.isString()) {
+                final String argStr = argValueRepr.asString();
                 return encodableFacet.fromEncodedString(argStr);
             }
             // give up
-            throwIncompatibleException(objectSpec, representation);
+            throwIncompatibleException(objectSpec, argRepr);
         }
 
         if (isLong(objectSpec)) {
-            if (!representation.isLong()) {
-                throwIncompatibleException(objectSpec, representation);
+            if (!argValueRepr.isLong()) {
+                throwIncompatibleException(objectSpec, argRepr);
             }
-            final String argStr = "" + representation.asLong();
+            final String argStr = "" + argValueRepr.asLong();
             return encodableFacet.fromEncodedString(argStr);
         }
 
         if (isBigInteger(objectSpec)) {
-            if (representation.isBigInteger()) {
-                final String argStr = "" + representation.asBigInteger();
+            if (argValueRepr.isBigInteger()) {
+                final String argStr = "" + argValueRepr.asBigInteger();
                 return encodableFacet.fromEncodedString(argStr);
             }
             // best effort
-            if (representation.isLong()) {
-                final String argStr = "" + representation.asLong();
+            if (argValueRepr.isLong()) {
+                final String argStr = "" + argValueRepr.asLong();
                 return encodableFacet.fromEncodedString(argStr);
             }
-            if (representation.isInt()) {
-                final String argStr = "" + representation.asInt();
+            if (argValueRepr.isInt()) {
+                final String argStr = "" + argValueRepr.asInt();
                 return encodableFacet.fromEncodedString(argStr);
             }
-            if (representation.isString()) {
-                final String argStr = representation.asString();
+            if (argValueRepr.isString()) {
+                final String argStr = argValueRepr.asString();
                 return encodableFacet.fromEncodedString(argStr);
             }
             // give up
-            throwIncompatibleException(objectSpec, representation);
+            throwIncompatibleException(objectSpec, argRepr);
         }
 
         if (isBigDecimal(objectSpec)) {
-            if (representation.isBigDecimal()) {
-                final String argStr = "" + representation.asBigDecimal();
+            if (argValueRepr.isBigDecimal()) {
+                final String argStr = "" + argValueRepr.asBigDecimal();
                 return encodableFacet.fromEncodedString(argStr);
             }
             // best effort
-            if (representation.isBigInteger()) {
-                final String argStr = "" + representation.asBigInteger();
+            if (argValueRepr.isBigInteger()) {
+                final String argStr = "" + argValueRepr.asBigInteger();
                 return encodableFacet.fromEncodedString(argStr);
             }
-            if (representation.isDouble()) {
-                final String argStr = "" + representation.asDouble();
+            if (argValueRepr.isDouble()) {
+                final String argStr = "" + argValueRepr.asDouble();
                 return encodableFacet.fromEncodedString(argStr);
             }
-            if (representation.isLong()) {
-                final String argStr = "" + representation.asLong();
+            if (argValueRepr.isLong()) {
+                final String argStr = "" + argValueRepr.asLong();
                 return encodableFacet.fromEncodedString(argStr);
             }
-            if (representation.isInt()) {
-                final String argStr = "" + representation.asInt();
+            if (argValueRepr.isInt()) {
+                final String argStr = "" + argValueRepr.asInt();
                 return encodableFacet.fromEncodedString(argStr);
             }
-            if (representation.isString()) {
-                final String argStr = representation.asString();
+            if (argValueRepr.isString()) {
+                final String argStr = argValueRepr.asString();
                 return encodableFacet.fromEncodedString(argStr);
             }
             // give up
-            throwIncompatibleException(objectSpec, representation);
+            throwIncompatibleException(objectSpec, argRepr);
         }
 
         if (isDouble(objectSpec)) {
-            if (representation.isDouble()) {
-                final String argStr = "" + representation.asDouble();
+            if (argValueRepr.isDouble()) {
+                final String argStr = "" + argValueRepr.asDouble();
                 return encodableFacet.fromEncodedString(argStr);
             }
             // best effort
-            if (representation.isLong()) {
-                final String argStr = "" + representation.asLong();
+            if (argValueRepr.isLong()) {
+                final String argStr = "" + argValueRepr.asLong();
                 return encodableFacet.fromEncodedString(argStr);
             }
-            if (representation.isInt()) {
-                final String argStr = "" + representation.asInt();
+            if (argValueRepr.isInt()) {
+                final String argStr = "" + argValueRepr.asInt();
                 return encodableFacet.fromEncodedString(argStr);
             }
-            if (representation.isString()) {
-                final String argStr = representation.asString();
+            if (argValueRepr.isString()) {
+                final String argStr = argValueRepr.asString();
                 return encodableFacet.fromEncodedString(argStr);
             }
             // give up
-            throwIncompatibleException(objectSpec, representation);
+            throwIncompatibleException(objectSpec, argRepr);
         }
 
-        if (!representation.isString()) {
-            throw new ExpectedStringRepresentingValueException();
+        if (argValueRepr.isString()) {
+            final String argStr = argValueRepr.asString();
+            return encodableFacet.fromEncodedString(argStr);
         }
-        final String argStr = representation.asString();
-        return encodableFacet.fromEncodedString(argStr);
+        
+        final String reason = "Unable to parse value";
+        argRepr.mapPut("invalidReason", reason);
+        throw new IllegalArgumentException(reason);
     }
 
-    public Object asObject(final ObjectAdapter objectAdapter) {
+    Object asObject(final ObjectAdapter objectAdapter) {
         if (objectAdapter == null) {
             throw new IllegalArgumentException("objectAdapter cannot be null");
         }
@@ -217,8 +229,10 @@ public final class JsonValueEncoder {
         return false;
     }
 
-    private void throwIncompatibleException(final ObjectSpecification objectSpec, final JsonRepresentation representation) {
-        throw new IllegalArgumentException(String.format("representation '%s' incompatible with objectSpec '%s'", representation.toString(), objectSpec.getCorrespondingClass().getName()));
+    private void throwIncompatibleException(final ObjectSpecification objectSpec, final JsonRepresentation argRepr) {
+        String reason = String.format("representation '%s' incompatible with objectSpec '%s'", argRepr.getMap("value").toString(), objectSpec.getCorrespondingClass().getName());
+        argRepr.mapPut("invalidReason", reason);
+        throw new IllegalArgumentException(reason);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java
index 3c147de..f288156 100644
--- a/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java
+++ b/component/viewer/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectActionReprRenderer.java
@@ -113,7 +113,7 @@ public class ObjectActionReprRenderer extends AbstractObjectMemberReprRenderer<O
         final JsonRepresentation argMap = JsonRepresentation.newMap();
         final List<ObjectActionParameter> parameters = objectMember.getParameters();
         for (int i = 0; i < objectMember.getParameterCount(); i++) {
-            argMap.mapPut(parameters.get(i).getId(), argValueFor(i));
+            argMap.mapPut(parameters.get(i).getId() + ".value", argValueFor(i));
         }
         return argMap;
     }

http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ResourceContext.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ResourceContext.java b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ResourceContext.java
index d7a4f58..27ec6c4 100644
--- a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ResourceContext.java
+++ b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ResourceContext.java
@@ -123,7 +123,7 @@ public class ResourceContext implements RendererContext {
     private void ensureDomainModelQueryParamSupported() {
         final DomainModel domainModel = getArg(RequestParameter.DOMAIN_MODEL);
         if(domainModel != DomainModel.FORMAL) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST,  
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST,  
                                            "x-ro-domain-model of '%s' is not supported", domainModel);
         }
     }

http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationException.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationException.java b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationException.java
index a9d1d83..01948f7 100644
--- a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationException.java
+++ b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationException.java
@@ -24,23 +24,23 @@ import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse.HttpS
 public class RestfulObjectsApplicationException extends RuntimeException implements HasHttpStatusCode {
 
     public static final RestfulObjectsApplicationException create(final HttpStatusCode httpStatusCode) {
-        return create(httpStatusCode, null);
+        return createWithCause(httpStatusCode, null);
     }
 
-    public static RestfulObjectsApplicationException create(final HttpStatusCode httpStatusCode, final String message, final Object... args) {
-        return create(httpStatusCode, (Exception) null, message, args);
+    public static RestfulObjectsApplicationException createWithMessage(final HttpStatusCode httpStatusCode, final String message, final Object... args) {
+        return createWithCauseAndMessage(httpStatusCode, (Exception) null, message, args);
     }
 
-    public static RestfulObjectsApplicationException create(final HttpStatusCode httpStatusCode, final Exception cause) {
-        return create(httpStatusCode, cause, null);
+    public static RestfulObjectsApplicationException createWithCause(final HttpStatusCode httpStatusCode, final Exception cause) {
+        return createWithCauseAndMessage(httpStatusCode, cause, null);
     }
 
-    public static RestfulObjectsApplicationException create(final HttpStatusCode httpStatusCode, final Exception cause, final String message, final Object... args) {
+    public static RestfulObjectsApplicationException createWithCauseAndMessage(final HttpStatusCode httpStatusCode, final Exception cause, final String message, final Object... args) {
         return new RestfulObjectsApplicationException(httpStatusCode, formatString(message, args), cause, null);
     }
 
-    public static RestfulObjectsApplicationException create(final HttpStatusCode httpStatusCode, final JsonRepresentation repr, final String message, final Object... args) {
-        return new RestfulObjectsApplicationException(httpStatusCode, formatString(message, args), null, repr);
+    public static RestfulObjectsApplicationException createWithBody(final HttpStatusCode httpStatusCode, final JsonRepresentation body, final String message, final Object... args) {
+        return new RestfulObjectsApplicationException(httpStatusCode, formatString(message, args), null, body);
     }
 
     private static String formatString(final String formatStr, final Object... args) {
@@ -49,12 +49,12 @@ public class RestfulObjectsApplicationException extends RuntimeException impleme
 
     private static final long serialVersionUID = 1L;
     private final HttpStatusCode httpStatusCode;
-    private final JsonRepresentation jsonRepresentation;
+    private final JsonRepresentation body;
 
-    private RestfulObjectsApplicationException(final HttpStatusCode httpStatusCode, final String message, final Throwable ex, final JsonRepresentation jsonRepresentation) {
-        super(message, ex);
+    private RestfulObjectsApplicationException(final HttpStatusCode httpStatusCode, final String message, final Throwable cause, final JsonRepresentation body) {
+        super(message, cause);
         this.httpStatusCode = httpStatusCode;
-        this.jsonRepresentation = jsonRepresentation;
+        this.body = body;
     }
 
     @Override
@@ -62,8 +62,8 @@ public class RestfulObjectsApplicationException extends RuntimeException impleme
         return httpStatusCode;
     }
 
-    public JsonRepresentation getJsonRepresentation() {
-        return jsonRepresentation;
+    public JsonRepresentation getBody() {
+        return body;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapper.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapper.java b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapper.java
index 2b9bbc2..ce6213d 100644
--- a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapper.java
+++ b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapper.java
@@ -20,6 +20,7 @@ package org.apache.isis.viewer.restfulobjects.server;
 
 import java.util.List;
 
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.ResponseBuilder;
 import javax.ws.rs.ext.ExceptionMapper;
@@ -37,21 +38,26 @@ import org.apache.isis.viewer.restfulobjects.applib.util.JsonMapper;
 @Provider
 public class RestfulObjectsApplicationExceptionMapper implements ExceptionMapper<RestfulObjectsApplicationException> {
 
-    private static final String WARNING_HEADER_PREFIX = "199 RestfulObjects ";
-
     @Override
     public Response toResponse(final RestfulObjectsApplicationException ex) {
-        final ResponseBuilder builder = Response.status(ex.getHttpStatusCode().getJaxrsStatusType()).type(RestfulMediaType.APPLICATION_JSON_ERROR).entity(jsonFor(ex));
+        final ResponseBuilder builder = Response.status(ex.getHttpStatusCode().getJaxrsStatusType());
+        final String body = bodyFor(ex);
+        if(body != null) {
+            builder.entity(body);
+            builder.type(MediaType.APPLICATION_JSON); // generic; the spec doesn't define what the media type should be
+        } else {
+            builder.type(RestfulMediaType.APPLICATION_JSON_ERROR);
+        }
         final String message = ex.getMessage();
         if (message != null) {
-            builder.header(RestfulResponse.Header.WARNING.getName(), WARNING_HEADER_PREFIX + message);
+            builder.header(RestfulResponse.Header.WARNING.getName(), RestfulResponse.Header.WARNING.render(message));
         }
         return builder.build();
     }
 
     private static class ExceptionPojo {
 
-        public static ExceptionPojo create(final Exception ex) {
+        public static ExceptionPojo create(final Throwable ex) {
             return new ExceptionPojo(ex);
         }
 
@@ -107,16 +113,20 @@ public class RestfulObjectsApplicationExceptionMapper implements ExceptionMapper
 
     }
 
-    static String jsonFor(final RestfulObjectsApplicationException ex) {
-        final JsonRepresentation jsonRepresentation = ex.getJsonRepresentation();
+    static String bodyFor(final RestfulObjectsApplicationException ex) {
+        final JsonRepresentation jsonRepresentation = ex.getBody();
         if (jsonRepresentation != null) {
             return jsonRepresentation.toString();
         }
+        Throwable cause = ex.getCause();
+        if(cause == null) {
+            return null;
+        }
         try {
-            return JsonMapper.instance().write(ExceptionPojo.create(ex));
+            return JsonMapper.instance().write(ExceptionPojo.create(cause));
         } catch (final Exception e) {
             // fallback
-            return "{ \"exception\": \"" + ExceptionUtils.getFullStackTrace(ex) + "\" }";
+            return "{ \"exception\": \"" + ExceptionUtils.getFullStackTrace(cause) + "\" }";
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainObjectResourceServerside.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainObjectResourceServerside.java b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainObjectResourceServerside.java
index 35ce708..24ff44f 100644
--- a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainObjectResourceServerside.java
+++ b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainObjectResourceServerside.java
@@ -68,27 +68,27 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
         final String objectStr = DomainResourceHelper.asStringUtf8(object);
         final JsonRepresentation objectRepr = DomainResourceHelper.readAsMap(objectStr);
         if (!objectRepr.isMap()) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "Body is not a map; got %s", objectRepr);
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Body is not a map; got %s", objectRepr);
         }
 
         final ObjectSpecification domainTypeSpec = getSpecificationLoader().lookupBySpecId(ObjectSpecId.of(domainType));
         if (domainTypeSpec == null) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "Could not determine type of domain object to persist (no class with domainType Id of '%s')", domainType);
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Could not determine type of domain object to persist (no class with domainType Id of '%s')", domainType);
         }
 
         final ObjectAdapter objectAdapter = getResourceContext().getPersistenceSession().createTransientInstance(domainTypeSpec);
 
         final JsonRepresentation propertiesList = objectRepr.getArrayEnsured("members[memberType=property]");
         if (propertiesList == null) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "Could not find properties list (no members[memberType=property]); got %s", objectRepr);
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Could not find properties list (no members[memberType=property]); got %s", objectRepr);
         }
         if (!DomainResourceHelper.copyOverProperties(getResourceContext(), objectAdapter, propertiesList)) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, objectRepr, "Illegal property value");
+            throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.BAD_REQUEST, objectRepr, "Illegal property value");
         }
 
         final Consent validity = objectAdapter.getSpecification().isValid(objectAdapter);
         if (validity.isVetoed()) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, objectRepr, validity.getReason());
+            throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.BAD_REQUEST, objectRepr, validity.getReason());
         }
         getResourceContext().getPersistenceSession().makePersistent(objectAdapter);
 
@@ -124,14 +124,14 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
         final String objectStr = DomainResourceHelper.asStringUtf8(object);
         final JsonRepresentation objectRepr = DomainResourceHelper.readAsMap(objectStr);
         if (!objectRepr.isMap()) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "Body is not a map; got %s", objectRepr);
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Body is not a map; got %s", objectRepr);
         }
 
         final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, oidStr);
 
         final JsonRepresentation propertiesList = objectRepr.getArrayEnsured("members[memberType=property]");
         if (propertiesList == null) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "Could not find properties list (no members[memberType=property]); got %s", objectRepr);
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Could not find properties list (no members[memberType=property]); got %s", objectRepr);
         }
 
         final IsisTransactionManager transactionManager = getResourceContext().getPersistenceSession().getTransactionManager();
@@ -139,13 +139,13 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
         try {
             if (!DomainResourceHelper.copyOverProperties(getResourceContext(), objectAdapter, propertiesList)) {
                 transactionManager.abortTransaction();
-                throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, objectRepr, "Illegal property value");
+                throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.BAD_REQUEST, objectRepr, "Illegal property value");
             }
 
             final Consent validity = objectAdapter.getSpecification().isValid(objectAdapter);
             if (validity.isVetoed()) {
                 transactionManager.abortTransaction();
-                throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, objectRepr, validity.getReason());
+                throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.BAD_REQUEST, objectRepr, validity.getReason());
             }
 
             transactionManager.endTransaction();
@@ -198,7 +198,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
 
         final Consent consent = property.isAssociationValid(objectAdapter, argAdapter);
         if (consent.isVetoed()) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.UNAUTHORIZED, consent.getReason());
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.UNAUTHORIZED, consent.getReason());
         }
 
         property.set(objectAdapter, argAdapter);
@@ -220,7 +220,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
 
         final Consent consent = property.isAssociationValid(objectAdapter, null);
         if (consent.isVetoed()) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.UNAUTHORIZED, consent.getReason());
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.UNAUTHORIZED, consent.getReason());
         }
 
         property.set(objectAdapter, null);
@@ -259,7 +259,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
         final OneToManyAssociation collection = helper.getCollectionThatIsVisibleAndUsable(collectionId, Intent.MUTATE, getResourceContext().getWhere());
 
         if (!collection.getCollectionSemantics().isSet()) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "Collection '%s' does not have set semantics", collectionId);
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Collection '%s' does not have set semantics", collectionId);
         }
 
         final ObjectSpecification collectionSpec = collection.getSpecification();
@@ -268,7 +268,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
 
         final Consent consent = collection.isValidToAdd(objectAdapter, argAdapter);
         if (consent.isVetoed()) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.UNAUTHORIZED, consent.getReason());
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.UNAUTHORIZED, consent.getReason());
         }
 
         collection.addElement(objectAdapter, argAdapter);
@@ -290,7 +290,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
         final OneToManyAssociation collection = helper.getCollectionThatIsVisibleAndUsable(collectionId, Intent.MUTATE, getResourceContext().getWhere());
 
         if (!collection.getCollectionSemantics().isListOrArray()) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.METHOD_NOT_ALLOWED, "Collection '%s' does not have list or array semantics", collectionId);
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED, "Collection '%s' does not have list or array semantics", collectionId);
         }
 
         final ObjectSpecification collectionSpec = collection.getSpecification();
@@ -299,7 +299,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
 
         final Consent consent = collection.isValidToAdd(objectAdapter, argAdapter);
         if (consent.isVetoed()) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.UNAUTHORIZED, consent.getReason());
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.UNAUTHORIZED, consent.getReason());
         }
 
         collection.addElement(objectAdapter, argAdapter);
@@ -324,7 +324,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
 
         final Consent consent = collection.isValidToRemove(objectAdapter, argAdapter);
         if (consent.isVetoed()) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.UNAUTHORIZED, consent.getReason());
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.UNAUTHORIZED, consent.getReason());
         }
 
         collection.removeElement(objectAdapter, argAdapter);

http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainResourceHelper.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainResourceHelper.java b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainResourceHelper.java
index b2ce5df..b8218d1 100644
--- a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainResourceHelper.java
+++ b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainResourceHelper.java
@@ -32,7 +32,6 @@ import org.apache.isis.applib.annotation.ActionSemantics;
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.core.commons.authentication.AuthenticationSession;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.adapter.version.Version;
 import org.apache.isis.core.metamodel.consent.Consent;
 import org.apache.isis.core.metamodel.facets.object.value.ValueFacet;
@@ -54,7 +53,6 @@ import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ActionResul
 import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.DomainObjectLinkTo;
 import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.DomainObjectReprRenderer;
 import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.JsonValueEncoder;
-import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.JsonValueEncoder.ExpectedStringRepresentingValueException;
 import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.MemberType;
 import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectActionReprRenderer;
 import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectAdapterLinkTo;
@@ -249,7 +247,7 @@ public final class DomainResourceHelper {
 
         final ActionSemantics.Of actionSemantics = action.getSemantics();
         if (actionSemantics != ActionSemantics.Of.SAFE) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.METHOD_NOT_ALLOWED, "Method not allowed; action '%s' is not query only", action.getId());
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED, "Method not allowed; action '%s' is not query only", action.getId());
         }
 
         return invokeActionUsingAdapters(action, arguments);
@@ -261,7 +259,7 @@ public final class DomainResourceHelper {
 
         final ActionSemantics.Of actionSemantics = action.getSemantics();
         if (!actionSemantics.isIdempotentInNature()) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.METHOD_NOT_ALLOWED, "Method not allowed; action '%s' is not idempotent", action.getId());
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED, "Method not allowed; action '%s' is not idempotent", action.getId());
         }
         return invokeActionUsingAdapters(action, arguments);
     }
@@ -290,7 +288,7 @@ public final class DomainResourceHelper {
                 final Object arg = argAdapter.getObject();
                 final String reasonNotValid = parameter.isValid(objectAdapter, arg, null);
                 if (reasonNotValid != null) {
-                    throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_ACCEPTABLE, reasonNotValid);
+                    throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.NOT_ACCEPTABLE, reasonNotValid);
                 }
             }
         }
@@ -299,7 +297,7 @@ public final class DomainResourceHelper {
         final ObjectAdapter[] argArray = argAdapters.toArray(new ObjectAdapter[0]);
         final Consent consent = action.isProposedArgumentSetValid(objectAdapter, argArray);
         if (consent.isVetoed()) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_ACCEPTABLE, consent.getReason());
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.NOT_ACCEPTABLE, consent.getReason());
         }
 
         // invoke
@@ -323,33 +321,46 @@ public final class DomainResourceHelper {
      * @param resourceContext
      * @param objectSpec
      *            - the {@link ObjectSpecification} to interpret the object as.
-     * @param representation
+     * @param argRepr
      *            - expected to be either a String or a Map (ie from within a
      *            List, built by parsing a JSON structure).
      */
-    private static ObjectAdapter objectAdapterFor(final RendererContext resourceContext, final ObjectSpecification objectSpec, final JsonRepresentation representation) {
+    private static ObjectAdapter objectAdapterFor(final RendererContext resourceContext, final ObjectSpecification objectSpec, final JsonRepresentation argRepr) {
 
-        if (representation == null) {
+        if (argRepr == null) {
             return null;
         }
 
         // value (encodable)
         if (objectSpec.isEncodeable()) {
-            return new JsonValueEncoder().asAdapter(objectSpec, representation);
+            return new JsonValueEncoder().asAdapter(objectSpec, argRepr);
+        }
+
+        final JsonRepresentation argValueRepr = argRepr.getRepresentation("value");
+        if(argValueRepr == null) {
+            String reason = "No 'value' key";
+            argRepr.mapPut("invalidReason", reason);
+            throw new IllegalArgumentException(reason);
         }
 
         // reference
-        if (!representation.isLink()) {
-            throw new IllegalArgumentException("Expected a link (because this object's type is not a value) but found no 'href'");
+        if (!argValueRepr.isLink()) {
+            final String reason = "Expected a link (because this object's type is not a value) but found no 'href'";
+            argRepr.mapPut("invalidReason", reason);
+            throw new IllegalArgumentException(reason);
         }
-        final String oidFromHref = UrlParserUtils.encodedOidFromLink(representation);
+        final String oidFromHref = UrlParserUtils.encodedOidFromLink(argValueRepr);
         if (oidFromHref == null) {
-            throw new IllegalArgumentException("Could not parse 'href' to identify the object's OID");
+            final String reason = "Could not parse 'href' to identify the object's OID";
+            argRepr.mapPut("invalidReason", reason);
+            throw new IllegalArgumentException(reason);
         }
 
         final ObjectAdapter objectAdapter = OidUtils.getObjectAdapterElseNull(resourceContext, oidFromHref);
         if (objectAdapter == null) {
-            throw new IllegalArgumentException("Object not found for 'href'");
+            final String reason = "'href' does not reference a known entity";
+            argRepr.mapPut("invalidReason", reason);
+            throw new IllegalArgumentException(reason);
         }
         return objectAdapter;
     }
@@ -416,7 +427,7 @@ public final class DomainResourceHelper {
             final Consent usable = objectMember.isUsable(authenticationSession, objectAdapter, where);
             if (usable.isVetoed()) {
                 final String memberTypeStr = memberType.name().toLowerCase();
-                throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_ACCEPTABLE, "%s is not usable: '%s' (%s)", memberTypeStr, memberId, usable.getReason());
+                throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.NOT_ACCEPTABLE, "%s is not usable: '%s' (%s)", memberTypeStr, memberId, usable.getReason());
             }
         }
         return objectMember;
@@ -424,7 +435,7 @@ public final class DomainResourceHelper {
 
     protected static void throwNotFoundException(final String memberId, final MemberType memberType) {
         final String memberTypeStr = memberType.name().toLowerCase();
-        throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND, "%s '%s' either does not exist or is not visible", memberTypeStr, memberId);
+        throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.NOT_FOUND, "%s '%s' either does not exist or is not visible", memberTypeStr, memberId);
     }
 
     // ///////////////////////////////////////////////////////////////////
@@ -447,7 +458,7 @@ public final class DomainResourceHelper {
     ObjectAdapter parseAsMapWithSingleValue(final ObjectSpecification objectSpec, final JsonRepresentation arguments) {
         final JsonRepresentation representation = arguments.getRepresentation("value");
         if (arguments.size() != 1 || representation == null) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "Body should be a map with a single key 'value' whose value represents an instance of type '%s'", resourceFor(objectSpec));
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Body should be a map with a single key 'value' whose value represents an instance of type '%s'", resourceFor(objectSpec));
         }
 
         return objectAdapterFor(resourceContext, objectSpec, representation);
@@ -462,19 +473,23 @@ public final class DomainResourceHelper {
 
         final List<ObjectAdapter> argAdapters = Lists.newArrayList();
         final List<ObjectActionParameter> parameters = action.getParameters();
+        final StringBuilder invalidReasonBuf = new StringBuilder();
         for (int i = 0; i < argList.size(); i++) {
-            final String paramName = parameters.get(i).getName();
             final JsonRepresentation arg = argList.get(i);
             final ObjectSpecification paramSpec = parameters.get(i).getSpecification();
             try {
                 final ObjectAdapter objectAdapter = objectAdapterFor(resourceContext, paramSpec, arg);
                 argAdapters.add(objectAdapter);
-            } catch (final ExpectedStringRepresentingValueException e) {
-                throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "Action '%s', argument %s should be a URL encoded string representing a value of type %s", action.getId(), paramName, resourceFor(paramSpec));
-            } catch (final ExpectedMapRepresentingLinkException e) {
-                throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "Action '%s', argument %s should be a map representing a link to reference of type %s", action.getId(), paramName, resourceFor(paramSpec));
+            } catch (final IllegalArgumentException e) {
+                if(invalidReasonBuf.length()>0) {
+                    invalidReasonBuf.append("; ");
+                }
+                invalidReasonBuf.append(e.getMessage());
             }
         }
+        if(invalidReasonBuf.length()>0) {
+            throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.VALIDATION_FAILED, arguments, invalidReasonBuf.toString());
+        }
         return argAdapters;
     }
 
@@ -485,7 +500,9 @@ public final class DomainResourceHelper {
         for (final Entry<String, JsonRepresentation> arg : arguments.mapIterable()) {
             final String argName = arg.getKey();
             if (action.getParameterById(argName) == null) {
-                throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "Action '%s' does not have a parameter %s but an argument of that name was provided", action.getId(), argName);
+                String reason = String.format("Action '%s' does not have a parameter %s but an argument of that name was provided", action.getId(), argName);
+                arguments.mapPut("x-ro-invalidReason", reason);
+                throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.VALIDATION_FAILED, reason);
             }
         }
 
@@ -496,7 +513,9 @@ public final class DomainResourceHelper {
             final String paramId = param.getId();
             final JsonRepresentation argRepr = arguments.getRepresentation(paramId);
             if (argRepr == null && !param.isOptional()) {
-                throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "Action '%s', no argument found for (mandatory) parameter '%s'", action.getId(), paramId);
+                String reason = String.format("Action '%s', no argument found for (mandatory) parameter '%s'", action.getId(), paramId);
+                arguments.mapPut("x-ro-invalidReason", reason);
+                throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.VALIDATION_FAILED, reason);
             }
             argList.add(argRepr);
         }
@@ -542,15 +561,15 @@ public final class DomainResourceHelper {
         try {
             final JsonRepresentation jsonRepr = JsonMapper.instance().read(args);
             if (!jsonRepr.isMap()) {
-                throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "could not read %s as a JSON map", argsNature);
+                throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "could not read %s as a JSON map", argsNature);
             }
             return jsonRepr;
         } catch (final JsonParseException e) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, e, "could not parse %s", argsNature);
+            throw RestfulObjectsApplicationException.createWithCauseAndMessage(HttpStatusCode.BAD_REQUEST, e, "could not parse %s", argsNature);
         } catch (final JsonMappingException e) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, e, "could not read %s as JSON", argsNature);
+            throw RestfulObjectsApplicationException.createWithCauseAndMessage(HttpStatusCode.BAD_REQUEST, e, "could not read %s as JSON", argsNature);
         } catch (final IOException e) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, e, "could not parse %s", argsNature);
+            throw RestfulObjectsApplicationException.createWithCauseAndMessage(HttpStatusCode.BAD_REQUEST, e, "could not parse %s", argsNature);
         }
     }
 
@@ -559,7 +578,7 @@ public final class DomainResourceHelper {
             final byte[] byteArray = ByteStreams.toByteArray(body);
             return new String(byteArray, Charsets.UTF_8);
         } catch (final IOException e) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, e, "could not read body");
+            throw RestfulObjectsApplicationException.createWithCauseAndMessage(HttpStatusCode.BAD_REQUEST, e, "could not read body");
         }
     }
 

http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java
index 839b84f..1d2de44 100644
--- a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java
+++ b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java
@@ -280,7 +280,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
     private static String linkFromFormalArgs(final String argumentsQueryString, final String paramName) {
         final JsonRepresentation arguments = DomainResourceHelper.readQueryStringAsMap(argumentsQueryString);
         if (!arguments.isLink(paramName)) {
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "Args should contain a link '%s'", paramName);
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Args should contain a link '%s'", paramName);
         }
 
         return arguments.getLink(paramName).getHref();

http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/ResourceAbstract.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/ResourceAbstract.java b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/ResourceAbstract.java
index b3a5a69..9f00f5b 100644
--- a/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/ResourceAbstract.java
+++ b/component/viewer/restfulobjects/server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/ResourceAbstract.java
@@ -174,7 +174,7 @@ public abstract class ResourceAbstract {
 
         if (objectAdapter == null) {
             final String instanceIdUnencoded = UrlDecoderUtils.urlDecode(instanceId);
-            throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND, "could not determine adapter for OID: '%s:%s'", domainType, instanceIdUnencoded);
+            throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.NOT_FOUND, "could not determine adapter for OID: '%s:%s'", domainType, instanceIdUnencoded);
         }
         return objectAdapter;
     }
@@ -197,7 +197,7 @@ public abstract class ResourceAbstract {
                 return serviceAdapter;
             }
         }
-        throw RestfulObjectsApplicationException.create(HttpStatusCode.NOT_FOUND, "Could not locate service '%s'", serviceId);
+        throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.NOT_FOUND, "Could not locate service '%s'", serviceId);
     }
 
 

http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/server/src/test/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapperTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/server/src/test/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapperTest.java b/component/viewer/restfulobjects/server/src/test/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapperTest.java
index a94d987..6aa9834 100644
--- a/component/viewer/restfulobjects/server/src/test/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapperTest.java
+++ b/component/viewer/restfulobjects/server/src/test/java/org/apache/isis/viewer/restfulobjects/server/RestfulObjectsApplicationExceptionMapperTest.java
@@ -71,7 +71,7 @@ public class RestfulObjectsApplicationExceptionMapperTest {
     public void entity_withMessage() throws Exception {
 
         // givens
-        final RestfulObjectsApplicationException ex = RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, "foobar");
+        final RestfulObjectsApplicationException ex = RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "foobar");
 
         // when
         final Response response = exceptionMapper.toResponse(ex);
@@ -92,7 +92,7 @@ public class RestfulObjectsApplicationExceptionMapperTest {
     public void entity_withCause() throws Exception {
         // given
         final Exception cause = new Exception("barfoo");
-        final RestfulObjectsApplicationException ex = RestfulObjectsApplicationException.create(HttpStatusCode.BAD_REQUEST, cause, "foobar");
+        final RestfulObjectsApplicationException ex = RestfulObjectsApplicationException.createWithCauseAndMessage(HttpStatusCode.BAD_REQUEST, cause, "foobar");
 
         // when
         final Response response = exceptionMapper.toResponse(ex);

http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_refarg_bad.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_refarg_bad.java b/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_refarg_bad.java
index a766d4e..3bcb277 100644
--- a/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_refarg_bad.java
+++ b/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_refarg_bad.java
@@ -18,11 +18,10 @@
  */
 package org.apache.isis.viewer.restfulobjects.tck.domainservice.serviceId.action.invoke;
 
-import static org.apache.isis.viewer.restfulobjects.tck.RestfulMatchers.isLink;
+import static org.apache.isis.viewer.restfulobjects.tck.RestfulMatchers.hasProfile;
+import static org.apache.isis.viewer.restfulobjects.tck.RestfulMatchers.hasStatus;
 import static org.hamcrest.CoreMatchers.*;
 import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.CoreMatchers.nullValue;
 import static org.junit.Assert.assertThat;
 
 import javax.ws.rs.core.MediaType;
@@ -30,7 +29,6 @@ import javax.ws.rs.core.Response;
 
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.LinkRepresentation;
-import org.apache.isis.viewer.restfulobjects.applib.Rel;
 import org.apache.isis.viewer.restfulobjects.applib.RestfulMediaType;
 import org.apache.isis.viewer.restfulobjects.applib.client.RestfulClient;
 import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse;
@@ -42,14 +40,9 @@ import org.apache.isis.viewer.restfulobjects.applib.domainobjects.DomainServiceR
 import org.apache.isis.viewer.restfulobjects.applib.domainobjects.ListRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.domainobjects.ObjectActionRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.domainobjects.ScalarValueRepresentation;
-import org.apache.isis.viewer.restfulobjects.applib.errors.ErrorRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.util.UrlEncodingUtils;
 import org.apache.isis.viewer.restfulobjects.tck.IsisWebServerRule;
-import org.apache.isis.viewer.restfulobjects.tck.RestfulMatchers;
-
-import static org.apache.isis.viewer.restfulobjects.tck.RestfulMatchers.*;
 import org.apache.isis.viewer.restfulobjects.tck.Util;
-import org.hamcrest.Matchers;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Rule;
@@ -67,7 +60,6 @@ public class DomainServiceTest_req_safe_refarg_bad {
     @Before
     public void setUp() throws Exception {
         client = webServerRule.getClient();
-
         serviceResource = client.getDomainServiceResource();
     }
 
@@ -86,26 +78,24 @@ public class DomainServiceTest_req_safe_refarg_bad {
         final JsonRepresentation args = invokeLink.getArguments();
         
         // when query the 'contains' action passing in the reference to the non-existent entity 
-        args.mapPut("searchFor", nonExistentEntityLink);
-        args.mapPut("from", 0);
-        args.mapPut("to", 1);
+        args.mapPut("searchFor.value", nonExistentEntityLink);
+        args.mapPut("from.value", 0);
+        args.mapPut("to.value", 1);
         
         RestfulResponse<ActionResultRepresentation> restfulResponse = client.followT(invokeLink, args);
 
         // then the response is an error
         assertThat(restfulResponse, hasStatus(HttpStatusCode.VALIDATION_FAILED));
 
-        assertThat(restfulResponse.getHeader(Header.WARNING), is("199 Argument 'searchFor' href does not reference a known entity"));
+        assertThat(restfulResponse.getHeader(Header.WARNING), is("'href' does not reference a known entity"));
 
-        // hmmm... what is the media type, though?  the spec doesn't say.  just assuming generic for now.
+        // hmmm... what is the media type, though?  the spec doesn't say.  testing for a generic one.
         assertThat(restfulResponse.getHeader(Header.CONTENT_TYPE), hasProfile(MediaType.APPLICATION_JSON));
 
         RestfulResponse<JsonRepresentation> restfulResponseOfError = restfulResponse.wraps(JsonRepresentation.class);
         JsonRepresentation repr = restfulResponseOfError.getEntity();
         
-
-        
-        
+        assertThat(repr.getString("searchFor.invalidReason"), is("'href' does not reference a known entity"));
     }
 
     @Ignore("still to update according to above test...")
@@ -119,9 +109,9 @@ public class DomainServiceTest_req_safe_refarg_bad {
         // when query the 'contains' action passing in the entity 
         // (for a range where the entity is contained in the range)
         JsonRepresentation args = JsonRepresentation.newMap();
-        args.mapPut("searchFor", firstEntityLink);
-        args.mapPut("from", 0);
-        args.mapPut("to", 3);
+        args.mapPut("searchFor.value", firstEntityLink);
+        args.mapPut("from.value", 0);
+        args.mapPut("to.value", 3);
         Response response = serviceResource.invokeActionQueryOnly("ActionsEntities", "contains", UrlEncodingUtils.urlEncode(args));
         RestfulResponse<ActionResultRepresentation> restfulResponse = RestfulResponse.ofT(response);
         

http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_refarg_resp_scalar.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_refarg_resp_scalar.java b/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_refarg_resp_scalar.java
index c9dad04..f7cd2c0 100644
--- a/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_refarg_resp_scalar.java
+++ b/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_refarg_resp_scalar.java
@@ -19,18 +19,16 @@
 package org.apache.isis.viewer.restfulobjects.tck.domainservice.serviceId.action.invoke;
 
 import static org.apache.isis.viewer.restfulobjects.tck.RestfulMatchers.isLink;
-import static org.hamcrest.CoreMatchers.*;
 import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
 import static org.junit.Assert.assertThat;
 
-import java.io.IOException;
-
 import javax.ws.rs.core.Response;
 
 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.LinkRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.Rel;
-import org.apache.isis.viewer.restfulobjects.applib.RestfulHttpMethod;
 import org.apache.isis.viewer.restfulobjects.applib.client.RestfulClient;
 import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse;
 import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse.HttpStatusCode;
@@ -42,13 +40,9 @@ import org.apache.isis.viewer.restfulobjects.applib.domainobjects.ObjectActionRe
 import org.apache.isis.viewer.restfulobjects.applib.domainobjects.ScalarValueRepresentation;
 import org.apache.isis.viewer.restfulobjects.applib.util.UrlEncodingUtils;
 import org.apache.isis.viewer.restfulobjects.tck.IsisWebServerRule;
-import org.apache.isis.viewer.restfulobjects.tck.RestfulMatchers;
 import org.apache.isis.viewer.restfulobjects.tck.Util;
-import org.codehaus.jackson.JsonParseException;
-import org.codehaus.jackson.map.JsonMappingException;
 import org.hamcrest.Matchers;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -58,7 +52,6 @@ public class DomainServiceTest_req_safe_refarg_resp_scalar {
     public IsisWebServerRule webServerRule = new IsisWebServerRule();
 
     private RestfulClient client;
-
     private DomainServiceResource serviceResource;
 
     @Before
@@ -87,9 +80,9 @@ public class DomainServiceTest_req_safe_refarg_resp_scalar {
         
         // when query the 'contains' action passing in the entity 
         // (for a range where the entity is contained in the range)
-        args.mapPut("searchFor", firstEntityLink);
-        args.mapPut("from", 0);
-        args.mapPut("to", 3);
+        args.mapPut("searchFor.value", firstEntityLink);
+        args.mapPut("from.value", 0);
+        args.mapPut("to.value", 3);
         
         RestfulResponse<ActionResultRepresentation> restfulResponse = client.followT(invokeLink, args);
 
@@ -114,9 +107,9 @@ public class DomainServiceTest_req_safe_refarg_resp_scalar {
         
         // and when query the 'contains' action for a different range which does not
         // contain the entity
-        args.mapPut("searchFor", firstEntityLink);
-        args.mapPut("from", 3);
-        args.mapPut("to", 5);
+        args.mapPut("searchFor.value", firstEntityLink);
+        args.mapPut("from.value", 3);
+        args.mapPut("to.values", 5);
         
         restfulResponse = client.followT(invokeLink, args);
 
@@ -139,9 +132,9 @@ public class DomainServiceTest_req_safe_refarg_resp_scalar {
         // when query the 'contains' action passing in the entity 
         // (for a range where the entity is contained in the range)
         JsonRepresentation args = JsonRepresentation.newMap();
-        args.mapPut("searchFor", firstEntityLink);
-        args.mapPut("from", 0);
-        args.mapPut("to", 3);
+        args.mapPut("searchFor.value", firstEntityLink);
+        args.mapPut("from.value", 0);
+        args.mapPut("to.value", 3);
         Response response = serviceResource.invokeActionQueryOnly("ActionsEntities", "contains", UrlEncodingUtils.urlEncode(args));
         RestfulResponse<ActionResultRepresentation> restfulResponse = RestfulResponse.ofT(response);
         
@@ -165,8 +158,8 @@ public class DomainServiceTest_req_safe_refarg_resp_scalar {
         final JsonRepresentation args = invokeLink.getArguments();
         
         // when
-        args.mapPut("from", from);
-        args.mapPut("to", to);
+        args.mapPut("from.value", from);
+        args.mapPut("to.value", to);
         
         final RestfulResponse<ActionResultRepresentation> restfulResponse = client.followT(invokeLink, args);
         

http://git-wip-us.apache.org/repos/asf/isis/blob/0ec704b6/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_simplearg_resp_list.java
----------------------------------------------------------------------
diff --git a/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_simplearg_resp_list.java b/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_simplearg_resp_list.java
index 1e8d02b..0e5c67c 100644
--- a/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_simplearg_resp_list.java
+++ b/component/viewer/restfulobjects/tck/src/test/java/org/apache/isis/viewer/restfulobjects/tck/domainservice/serviceId/action/invoke/DomainServiceTest_req_safe_simplearg_resp_list.java
@@ -81,8 +81,8 @@ public class DomainServiceTest_req_safe_simplearg_resp_list {
         assertThat(args, RestfulMatchers.mapHas("to"));
         
         // when
-        args.mapPut("from", 1);
-        args.mapPut("to", 3);
+        args.mapPut("from.value", 1);
+        args.mapPut("to.value", 3);
 
         final RestfulResponse<ActionResultRepresentation> restfulResponse = client.followT(invokeLink, args);
         
@@ -100,8 +100,8 @@ public class DomainServiceTest_req_safe_simplearg_resp_list {
 
         // given, when
         JsonRepresentation args = JsonRepresentation.newMap();
-        args.mapPut("from", 1);
-        args.mapPut("to", 3);
+        args.mapPut("from.value", 1);
+        args.mapPut("to.value", 3);
         Response response = serviceResource.invokeActionQueryOnly("ActionsEntities", "subList", UrlEncodingUtils.urlEncode(args));
         RestfulResponse<ActionResultRepresentation> restfulResponse = RestfulResponse.ofT(response);