You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2014/10/09 13:26:44 UTC
[2/6] git commit: ISIS-917: first cut at a pluggable representations
service for RO.
ISIS-917: first cut at a pluggable representations service for RO.
The app runs fine, but tests are failing.
Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/bfece661
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/bfece661
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/bfece661
Branch: refs/heads/master
Commit: bfece661b326a070f5d25cc814a539dbd11d97a8
Parents: b0e31f6
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Wed Oct 8 08:21:46 2014 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Wed Oct 8 08:21:46 2014 +0100
----------------------------------------------------------------------
.../ServicesInstallerFromAnnotation.java | 1 +
.../restfulobjects/server/ResourceContext.java | 4 +-
.../DomainObjectResourceServerside.java | 118 ++--
.../server/resources/DomainResourceHelper.java | 635 ++-----------------
.../DomainServiceResourceServerside.java | 12 +-
.../resources/DomainTypeResourceServerside.java | 18 +-
.../resources/HomePageResourceServerside.java | 2 +-
.../server/resources/JsonParserHelper.java | 141 ++++
.../server/resources/ObjectActionArgHelper.java | 122 ++++
.../resources/ObjectAdapterAccessHelper.java | 134 ++++
.../resources/ObjectAdapterUpdateHelper.java | 134 ++++
.../server/resources/ResourceAbstract.java | 57 +-
.../resources/ResponseGeneratorService.java | 273 ++++++++
.../server/resources/Responses.java | 64 ++
.../resources/UserResourceServerside.java | 2 +-
.../restfulobjects/server/resources/Util.java | 124 ++++
.../resources/VersionResourceServerside.java | 2 +-
.../DomainResourceHelperTest_readBodyAsMap.java | 12 +-
.../main/java/webapp/CustomMementoService.java | 61 ++
.../webapp/CustomResponseGeneratorService.java | 32 +
.../src/main/java/webapp/MyMementoService.java | 61 --
.../src/main/webapp/WEB-INF/isis.properties | 3 +-
22 files changed, 1228 insertions(+), 784 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/isis/blob/bfece661/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java
index 5bd15a2..e325c06 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java
@@ -64,6 +64,7 @@ public class ServicesInstallerFromAnnotation extends InstallerAbstract implement
",org.apache.isis.core.metamodel.services" +
",org.apache.isis.core.runtime.services" +
",org.apache.isis.objectstore.jdo.applib.service" +
+ ",org.apache.isis.viewer.restfulobjects.server.resources" +
",org.apache.isis.viewer.restfulobjects.rendering.eventserializer" +
",org.apache.isis.objectstore.jdo.datanucleus.service.support" +
",org.apache.isis.objectstore.jdo.datanucleus.service.eventbus";
http://git-wip-us.apache.org/repos/asf/isis/blob/bfece661/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ResourceContext.java
----------------------------------------------------------------------
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ResourceContext.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ResourceContext.java
index 31a3996..efa2d7a 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ResourceContext.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/ResourceContext.java
@@ -41,7 +41,7 @@ import org.apache.isis.viewer.restfulobjects.applib.client.RestfulRequest.Domain
import org.apache.isis.viewer.restfulobjects.applib.client.RestfulRequest.RequestParameter;
import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse.HttpStatusCode;
import org.apache.isis.viewer.restfulobjects.rendering.RendererContext;
-import org.apache.isis.viewer.restfulobjects.server.resources.DomainResourceHelper;
+import org.apache.isis.viewer.restfulobjects.server.resources.Util;
public class ResourceContext implements RendererContext {
@@ -190,7 +190,7 @@ public class ResourceContext implements RendererContext {
return map;
} else {
final String queryString = getUrlUnencodedQueryString();
- return DomainResourceHelper.readQueryStringAsMap(queryString);
+ return Util.readQueryStringAsMap(queryString);
}
}
http://git-wip-us.apache.org/repos/asf/isis/blob/bfece661/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainObjectResourceServerside.java
----------------------------------------------------------------------
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainObjectResourceServerside.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainObjectResourceServerside.java
index d985c8a..9d63296 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainObjectResourceServerside.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainObjectResourceServerside.java
@@ -17,21 +17,10 @@
package org.apache.isis.viewer.restfulobjects.server.resources;
import java.io.InputStream;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
+import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-
import org.jboss.resteasy.annotations.ClientResponseType;
-
import org.apache.isis.applib.annotation.Where;
import org.apache.isis.core.commons.url.UrlEncodingUtils;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
@@ -47,8 +36,6 @@ import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse;
import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse.HttpStatusCode;
import org.apache.isis.viewer.restfulobjects.applib.domainobjects.DomainObjectResource;
import org.apache.isis.viewer.restfulobjects.server.RestfulObjectsApplicationException;
-import org.apache.isis.viewer.restfulobjects.server.resources.DomainResourceHelper.Intent;
-import org.apache.isis.viewer.restfulobjects.server.resources.DomainResourceHelper.MemberMode;
@Path("/objects")
public class DomainObjectResourceServerside extends ResourceAbstract implements DomainObjectResource {
@@ -67,8 +54,8 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
init(RepresentationType.DOMAIN_OBJECT, Where.OBJECT_FORMS);
- final String objectStr = DomainResourceHelper.asStringUtf8(object);
- final JsonRepresentation objectRepr = DomainResourceHelper.readAsMap(objectStr);
+ final String objectStr = Util.asStringUtf8(object);
+ final JsonRepresentation objectRepr = Util.readAsMap(objectStr);
if (!objectRepr.isMap()) {
throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Body is not a map; got %s", objectRepr);
}
@@ -80,11 +67,14 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
final ObjectAdapter objectAdapter = getResourceContext().getPersistenceSession().createTransientInstance(domainTypeSpec);
+ final ObjectAdapterUpdateHelper updateHelper = new ObjectAdapterUpdateHelper(getResourceContext(), objectAdapter);
+
final JsonRepresentation propertiesList = objectRepr.getArrayEnsured("members[objectMemberType=property]");
if (propertiesList == null) {
throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Could not find properties list (no members[objectMemberType=property]); got %s", objectRepr);
}
- if (!DomainResourceHelper.copyOverProperties(getResourceContext(), objectAdapter, propertiesList)) {
+
+ if (!updateHelper.copyOverProperties(propertiesList)) {
throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.BAD_REQUEST, objectRepr, "Illegal property value");
}
@@ -94,7 +84,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
}
getResourceContext().getPersistenceSession().makePersistent(objectAdapter);
- return new DomainResourceHelper(getResourceContext(), objectAdapter).objectRepresentation();
+ return getDomainResourceHelper(objectAdapter).objectRepresentation();
}
// //////////////////////////////////////////////////////////
@@ -110,10 +100,14 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId);
- final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), objectAdapter);
+ final DomainResourceHelper helper = getDomainResourceHelper(objectAdapter);
return helper.objectRepresentation();
}
+ private DomainResourceHelper getDomainResourceHelper(ObjectAdapter objectAdapter) {
+ return new DomainResourceHelper(getResourceContext(), objectAdapter);
+ }
+
@Override
@PUT
@Path("/{domainType}/{instanceId}")
@@ -123,15 +117,16 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
init(RepresentationType.DOMAIN_OBJECT, Where.OBJECT_FORMS);
- final String objectStr = DomainResourceHelper.asStringUtf8(object);
- final JsonRepresentation argRepr = DomainResourceHelper.readAsMap(objectStr);
+ final String objectStr = Util.asStringUtf8(object);
+ final JsonRepresentation argRepr = Util.readAsMap(objectStr);
if (!argRepr.isMap()) {
throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Body is not a map; got %s", argRepr);
}
final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId);
+ final ObjectAdapterUpdateHelper updateHelper = new ObjectAdapterUpdateHelper(getResourceContext(), objectAdapter);
- if (!DomainResourceHelper.copyOverProperties(getResourceContext(), objectAdapter, argRepr)) {
+ if (!updateHelper.copyOverProperties(argRepr)) {
throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.BAD_REQUEST, argRepr, "Illegal property value");
}
@@ -140,7 +135,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.BAD_REQUEST, argRepr, validity.getReason());
}
- final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), objectAdapter);
+ final DomainResourceHelper helper = getDomainResourceHelper(objectAdapter);
return helper.objectRepresentation();
}
@@ -166,9 +161,9 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
init(RepresentationType.OBJECT_PROPERTY, Where.OBJECT_FORMS);
final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId);
- final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), objectAdapter);
+ final DomainResourceHelper helper = getDomainResourceHelper(objectAdapter);
- return helper.propertyDetails(propertyId, MemberMode.NOT_MUTATING, Caching.NONE, getResourceContext().getWhere());
+ return helper.propertyDetails(propertyId, ResponseGeneratorService.MemberMode.NOT_MUTATING, Caching.NONE);
}
@Override
@@ -180,14 +175,15 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
init(Where.OBJECT_FORMS);
final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId);
- final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), objectAdapter);
+ final DomainResourceHelper helper = getDomainResourceHelper(objectAdapter);
+ final ObjectAdapterAccessHelper accessHelper = new ObjectAdapterAccessHelper(getResourceContext(), objectAdapter);
- final OneToOneAssociation property = helper.getPropertyThatIsVisibleForIntent(propertyId, Intent.MUTATE, getResourceContext().getWhere());
+ final OneToOneAssociation property = accessHelper.getPropertyThatIsVisibleForIntent(propertyId, ObjectAdapterAccessHelper.Intent.MUTATE);
final ObjectSpecification propertySpec = property.getSpecification();
- final String bodyAsString = DomainResourceHelper.asStringUtf8(body);
+ final String bodyAsString = Util.asStringUtf8(body);
- final ObjectAdapter argAdapter = helper.parseAsMapWithSingleValue(propertySpec, bodyAsString);
+ final ObjectAdapter argAdapter = new JsonParserHelper(getResourceContext(), propertySpec).parseAsMapWithSingleValue(bodyAsString);
final Consent consent = property.isAssociationValid(objectAdapter, argAdapter);
if (consent.isVetoed()) {
@@ -196,7 +192,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
property.set(objectAdapter, argAdapter);
- return helper.propertyDetails(propertyId, MemberMode.MUTATING, Caching.NONE, getResourceContext().getWhere());
+ return helper.propertyDetails(propertyId, ResponseGeneratorService.MemberMode.MUTATING, Caching.NONE);
}
@Override
@@ -207,9 +203,11 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
init(Where.OBJECT_FORMS);
final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId);
- final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), objectAdapter);
+ final DomainResourceHelper helper = getDomainResourceHelper(objectAdapter);
+ final ObjectAdapterAccessHelper accessHelper = new ObjectAdapterAccessHelper(getResourceContext(), objectAdapter);
- final OneToOneAssociation property = helper.getPropertyThatIsVisibleForIntent(propertyId, Intent.MUTATE, getResourceContext().getWhere());
+ final OneToOneAssociation property = accessHelper.getPropertyThatIsVisibleForIntent(
+ propertyId, ObjectAdapterAccessHelper.Intent.MUTATE);
final Consent consent = property.isAssociationValid(objectAdapter, null);
if (consent.isVetoed()) {
@@ -218,7 +216,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
property.set(objectAdapter, null);
- return helper.propertyDetails(propertyId, MemberMode.MUTATING, Caching.NONE, getResourceContext().getWhere());
+ return helper.propertyDetails(propertyId, ResponseGeneratorService.MemberMode.MUTATING, Caching.NONE);
}
@Override
@@ -238,9 +236,9 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
init(RepresentationType.OBJECT_COLLECTION, Where.PARENTED_TABLES);
final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId);
- final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), objectAdapter);
+ final DomainResourceHelper helper = getDomainResourceHelper(objectAdapter);
- return helper.collectionDetails(collectionId, MemberMode.NOT_MUTATING, Caching.NONE, getResourceContext().getWhere());
+ return helper.collectionDetails(collectionId, ResponseGeneratorService.MemberMode.NOT_MUTATING, Caching.NONE);
}
@Override
@@ -252,17 +250,19 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
init(Where.PARENTED_TABLES);
final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId);
- final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), objectAdapter);
+ final DomainResourceHelper helper = getDomainResourceHelper(objectAdapter);
+ final ObjectAdapterAccessHelper accessHelper = new ObjectAdapterAccessHelper(getResourceContext(), objectAdapter);
- final OneToManyAssociation collection = helper.getCollectionThatIsVisibleForIntent(collectionId, Intent.MUTATE, getResourceContext().getWhere());
+ final OneToManyAssociation collection = accessHelper.getCollectionThatIsVisibleForIntent(
+ collectionId, ObjectAdapterAccessHelper.Intent.MUTATE);
if (!collection.getCollectionSemantics().isSet()) {
throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Collection '%s' does not have set semantics", collectionId);
}
final ObjectSpecification collectionSpec = collection.getSpecification();
- final String bodyAsString = DomainResourceHelper.asStringUtf8(body);
- final ObjectAdapter argAdapter = helper.parseAsMapWithSingleValue(collectionSpec, bodyAsString);
+ final String bodyAsString = Util.asStringUtf8(body);
+ final ObjectAdapter argAdapter = new JsonParserHelper(getResourceContext(), collectionSpec).parseAsMapWithSingleValue(bodyAsString);
final Consent consent = collection.isValidToAdd(objectAdapter, argAdapter);
if (consent.isVetoed()) {
@@ -271,7 +271,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
collection.addElement(objectAdapter, argAdapter);
- return helper.collectionDetails(collectionId, MemberMode.MUTATING, Caching.NONE, getResourceContext().getWhere());
+ return helper.collectionDetails(collectionId, ResponseGeneratorService.MemberMode.MUTATING, Caching.NONE);
}
@Override
@@ -283,17 +283,19 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
init(Where.PARENTED_TABLES);
final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId);
- final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), objectAdapter);
+ final DomainResourceHelper helper = getDomainResourceHelper(objectAdapter);
+ final ObjectAdapterAccessHelper accessHelper = new ObjectAdapterAccessHelper(getResourceContext(), objectAdapter);
- final OneToManyAssociation collection = helper.getCollectionThatIsVisibleForIntent(collectionId, Intent.MUTATE, getResourceContext().getWhere());
+ final OneToManyAssociation collection = accessHelper.getCollectionThatIsVisibleForIntent(
+ collectionId, ObjectAdapterAccessHelper.Intent.MUTATE);
if (!collection.getCollectionSemantics().isListOrArray()) {
throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED, "Collection '%s' does not have list or array semantics", collectionId);
}
final ObjectSpecification collectionSpec = collection.getSpecification();
- final String bodyAsString = DomainResourceHelper.asStringUtf8(body);
- final ObjectAdapter argAdapter = helper.parseAsMapWithSingleValue(collectionSpec, bodyAsString);
+ final String bodyAsString = Util.asStringUtf8(body);
+ final ObjectAdapter argAdapter = new JsonParserHelper(getResourceContext(), collectionSpec).parseAsMapWithSingleValue(bodyAsString);
final Consent consent = collection.isValidToAdd(objectAdapter, argAdapter);
if (consent.isVetoed()) {
@@ -302,7 +304,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
collection.addElement(objectAdapter, argAdapter);
- return helper.collectionDetails(collectionId, MemberMode.MUTATING, Caching.NONE, getResourceContext().getWhere());
+ return helper.collectionDetails(collectionId, ResponseGeneratorService.MemberMode.MUTATING, Caching.NONE);
}
@Override
@@ -313,12 +315,14 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
init(Where.PARENTED_TABLES);
final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId);
- final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), objectAdapter);
+ final DomainResourceHelper helper = getDomainResourceHelper(objectAdapter);
+ final ObjectAdapterAccessHelper accessHelper = new ObjectAdapterAccessHelper(getResourceContext(), objectAdapter);
- final OneToManyAssociation collection = helper.getCollectionThatIsVisibleForIntent(collectionId, Intent.MUTATE, getResourceContext().getWhere());
+ final OneToManyAssociation collection = accessHelper.getCollectionThatIsVisibleForIntent(
+ collectionId, ObjectAdapterAccessHelper.Intent.MUTATE);
final ObjectSpecification collectionSpec = collection.getSpecification();
- final ObjectAdapter argAdapter = helper.parseAsMapWithSingleValue(collectionSpec, getResourceContext().getUrlUnencodedQueryString());
+ final ObjectAdapter argAdapter = new JsonParserHelper(getResourceContext(), collectionSpec).parseAsMapWithSingleValue(getResourceContext().getUrlUnencodedQueryString());
final Consent consent = collection.isValidToRemove(objectAdapter, argAdapter);
if (consent.isVetoed()) {
@@ -327,7 +331,7 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
collection.removeElement(objectAdapter, argAdapter);
- return helper.collectionDetails(collectionId, MemberMode.MUTATING, Caching.NONE, getResourceContext().getWhere());
+ return helper.collectionDetails(collectionId, ResponseGeneratorService.MemberMode.MUTATING, Caching.NONE);
}
// //////////////////////////////////////////////////////////
@@ -342,9 +346,9 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
init(RepresentationType.OBJECT_ACTION, Where.OBJECT_FORMS);
final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId);
- final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), objectAdapter);
+ final DomainResourceHelper helper = getDomainResourceHelper(objectAdapter);
- return helper.actionPrompt(actionId, getResourceContext().getWhere());
+ return helper.actionPrompt(actionId);
}
@Override
@@ -382,9 +386,9 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
final JsonRepresentation arguments = getResourceContext().getQueryStringAsJsonRepr();
final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId);
- final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), objectAdapter);
+ final DomainResourceHelper helper = getDomainResourceHelper(objectAdapter);
- return helper.invokeActionQueryOnly(actionId, arguments, getResourceContext().getWhere());
+ return helper.invokeActionQueryOnly(actionId, arguments);
}
@Override
@@ -403,9 +407,9 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
final JsonRepresentation arguments = getResourceContext().getQueryStringAsJsonRepr();
final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId);
- final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), objectAdapter);
+ final DomainResourceHelper helper = getDomainResourceHelper(objectAdapter);
- return helper.invokeActionIdempotent(actionId, arguments, getResourceContext().getWhere());
+ return helper.invokeActionIdempotent(actionId, arguments);
}
@Override
@@ -419,10 +423,10 @@ public class DomainObjectResourceServerside extends ResourceAbstract implements
final JsonRepresentation arguments = getResourceContext().getQueryStringAsJsonRepr();
final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId);
- final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), objectAdapter);
+ final DomainResourceHelper helper = getDomainResourceHelper(objectAdapter);
Where where = getResourceContext().getWhere();
- return helper.invokeAction(actionId, arguments, where);
+ return helper.invokeAction(actionId, arguments);
}
@Override
http://git-wip-us.apache.org/repos/asf/isis/blob/bfece661/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainResourceHelper.java
----------------------------------------------------------------------
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainResourceHelper.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainResourceHelper.java
index 621f041..db04307 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainResourceHelper.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainResourceHelper.java
@@ -1,4 +1,3 @@
-
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -17,79 +16,27 @@
*/
package org.apache.isis.viewer.restfulobjects.server.resources;
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.ResponseBuilder;
-
-import com.google.common.base.Charsets;
-import com.google.common.collect.Lists;
-import com.google.common.io.ByteStreams;
-
-import org.codehaus.jackson.JsonParseException;
-import org.codehaus.jackson.map.JsonMappingException;
-
-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.version.Version;
-import org.apache.isis.core.metamodel.consent.Consent;
-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.Contributed;
-import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
-import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
-import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
-import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
-import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
-import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.runtime.system.persistence.PersistenceSession;
import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
-import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse.HttpStatusCode;
-import org.apache.isis.viewer.restfulobjects.applib.util.JsonMapper;
-import org.apache.isis.viewer.restfulobjects.applib.util.UrlEncodingUtils;
-import org.apache.isis.viewer.restfulobjects.rendering.RendererContext;
-import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.AbstractObjectMemberReprRenderer;
-import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ActionResultReprRenderer;
-import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ActionResultReprRenderer.SelfLink;
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.MemberType;
-import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectActionReprRenderer;
import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectAdapterLinkTo;
-import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectAndAction;
-import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectAndActionInvocation;
-import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectAndCollection;
-import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectAndProperty;
-import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectCollectionReprRenderer;
-import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.ObjectPropertyReprRenderer;
import org.apache.isis.viewer.restfulobjects.server.ResourceContext;
-import org.apache.isis.viewer.restfulobjects.server.RestfulObjectsApplicationException;
import org.apache.isis.viewer.restfulobjects.server.resources.ResourceAbstract.Caching;
-import org.apache.isis.viewer.restfulobjects.server.util.OidUtils;
-import org.apache.isis.viewer.restfulobjects.server.util.UrlDecoderUtils;
-import org.apache.isis.viewer.restfulobjects.server.util.UrlParserUtils;
-
-public final class DomainResourceHelper {
-
- private static final DateFormat ETAG_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
- private final RendererContext resourceContext;
- private ObjectAdapterLinkTo adapterLinkTo;
+public class DomainResourceHelper implements ResponseGeneratorService.ResponseContext {
- private final ObjectAdapter objectAdapter;
+ private final ResponseGeneratorService generatorService;
- public DomainResourceHelper(final RendererContext resourceContext, final ObjectAdapter objectAdapter) {
+ public DomainResourceHelper(final ResourceContext resourceContext, final ObjectAdapter objectAdapter) {
this.resourceContext = resourceContext;
this.objectAdapter = objectAdapter;
+
using(new DomainObjectLinkTo());
+
+ generatorService = lookupService(ResponseGeneratorService.class);
}
public DomainResourceHelper using(final ObjectAdapterLinkTo linkTo) {
@@ -98,563 +45,83 @@ public final class DomainResourceHelper {
return this;
}
- // //////////////////////////////////////////////////////////////
- // multiple properties (persist or multi-property update)
- // //////////////////////////////////////////////////////////////
-
- static boolean copyOverProperties(final RendererContext resourceContext, final ObjectAdapter objectAdapter, final JsonRepresentation propertiesList) {
- final ObjectSpecification objectSpec = objectAdapter.getSpecification();
- final List<ObjectAssociation> properties = objectSpec.getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.PROPERTIES);
- boolean allOk = true;
-
- for (final ObjectAssociation association : properties) {
- final OneToOneAssociation property = (OneToOneAssociation) association;
- final ObjectSpecification propertySpec = property.getSpecification();
- final String id = property.getId();
- final JsonRepresentation propertyRepr = propertiesList.getRepresentation(id);
- final Consent visibility = property.isVisible(resourceContext.getAuthenticationSession() , objectAdapter, resourceContext.getWhere());
- final Consent usability = property.isUsable(resourceContext.getAuthenticationSession() , objectAdapter, resourceContext.getWhere());
-
- final boolean invisible = visibility.isVetoed();
- final boolean disabled = usability.isVetoed();
- final boolean valueProvided = propertyRepr != null;
+ //region > ResponseContext impl
- if(!valueProvided) {
-
- // no value provided
-
- if(invisible || disabled) {
- // that's ok, indeed expected
- continue;
- }
- if (!property.isMandatory()) {
- // optional, so also not a problem
- continue;
- }
-
- // otherwise, is an error.
- final String invalidReason = propertiesList.getString("x-ro-invalidReason");
- if(invalidReason != null) {
- propertiesList.mapPut("x-ro-invalidReason", invalidReason + "; " + property.getName());
- } else {
- propertiesList.mapPut("x-ro-invalidReason", "Mandatory field(s) missing: " + property.getName());
- }
- allOk = false;
- continue;
-
- } else {
-
- // value has been provided
- if (invisible) {
- // silently ignore; don't want to acknowledge the existence of this property to the caller
- continue;
- }
- if (disabled) {
- // not allowed to update
- propertyRepr.mapPut("invalidReason", usability.getReason());
- allOk = false;
- continue;
- }
-
- // ok, we have a value, and the property is not invisible, and is not disabled
- final ObjectAdapter valueAdapter;
- try {
- valueAdapter = objectAdapterFor(resourceContext, propertySpec, propertyRepr);
- } catch(IllegalArgumentException ex) {
- propertyRepr.mapPut("invalidReason", ex.getMessage());
- allOk = false;
- continue;
- }
- // check if the proposed value is valid
- final Consent validity = property.isAssociationValid(objectAdapter, valueAdapter);
- if (validity.isAllowed()) {
- try {
- property.set(objectAdapter, valueAdapter);
- } catch (final IllegalArgumentException ex) {
- propertyRepr.mapPut("invalidReason", ex.getMessage());
- allOk = false;
- }
- } else {
- propertyRepr.mapPut("invalidReason", validity.getReason());
- allOk = false;
- }
- }
-
- }
-
- return allOk;
- }
-
- // //////////////////////////////////////////////////////////////
- // propertyDetails
- // //////////////////////////////////////////////////////////////
-
- public Response objectRepresentation() {
- final DomainObjectReprRenderer renderer = new DomainObjectReprRenderer(resourceContext, null, JsonRepresentation.newMap());
- renderer.with(objectAdapter).includesSelf();
-
- final ResponseBuilder respBuilder = ResourceAbstract.responseOfOk(renderer, Caching.NONE);
-
- final Version version = objectAdapter.getVersion();
- if (version != null && version.getTime() != null) {
- respBuilder.tag(ETAG_FORMAT.format(version.getTime()));
- }
- return respBuilder.build();
- }
-
- // //////////////////////////////////////////////////////////////
- // propertyDetails
- // //////////////////////////////////////////////////////////////
-
- public enum MemberMode {
- NOT_MUTATING {
- @Override
- public void apply(final AbstractObjectMemberReprRenderer<?, ?> renderer) {
- renderer.asStandalone();
- }
- },
- MUTATING {
- @Override
- public void apply(final AbstractObjectMemberReprRenderer<?, ?> renderer) {
- renderer.asMutated();
- }
- };
-
- public abstract void apply(AbstractObjectMemberReprRenderer<?, ?> renderer);
- }
-
- Response propertyDetails(final String propertyId, final MemberMode memberMode, final Caching caching, Where where) {
-
- final OneToOneAssociation property = getPropertyThatIsVisibleForIntent(propertyId, Intent.ACCESS, where);
-
- final ObjectPropertyReprRenderer renderer = new ObjectPropertyReprRenderer(resourceContext, null, null, JsonRepresentation.newMap());
-
- renderer.with(new ObjectAndProperty(objectAdapter, property)).usingLinkTo(adapterLinkTo);
-
- memberMode.apply(renderer);
-
- return ResourceAbstract.responseOfOk(renderer, caching).build();
- }
-
- // //////////////////////////////////////////////////////////////
- // collectionDetails
- // //////////////////////////////////////////////////////////////
-
- Response collectionDetails(final String collectionId, final MemberMode memberMode, final Caching caching, Where where) {
-
- final OneToManyAssociation collection = getCollectionThatIsVisibleForIntent(collectionId, Intent.ACCESS, where);
-
- final ObjectCollectionReprRenderer renderer = new ObjectCollectionReprRenderer(resourceContext, null, null, JsonRepresentation.newMap());
-
- renderer.with(new ObjectAndCollection(objectAdapter, collection)).usingLinkTo(adapterLinkTo);
-
- memberMode.apply(renderer);
-
- return ResourceAbstract.responseOfOk(renderer, caching).build();
- }
-
- // //////////////////////////////////////////////////////////////
- // action Prompt
- // //////////////////////////////////////////////////////////////
-
- Response actionPrompt(final String actionId, Where where) {
- final ObjectAction action = getObjectActionThatIsVisibleForIntent(actionId, Intent.ACCESS, where);
-
- final ObjectActionReprRenderer renderer = new ObjectActionReprRenderer(resourceContext, null, null, JsonRepresentation.newMap());
-
- renderer.with(new ObjectAndAction(objectAdapter, action)).usingLinkTo(adapterLinkTo).asStandalone();
+ private final ResourceContext resourceContext;
+ private final ObjectAdapter objectAdapter;
+ private ObjectAdapterLinkTo adapterLinkTo;
- return ResourceAbstract.responseOfOk(renderer, Caching.NONE).build();
+ @Override
+ public ResourceContext getResourceContext() {
+ return resourceContext;
}
- // //////////////////////////////////////////////////////////////
- // invoke action
- // //////////////////////////////////////////////////////////////
-
- enum Intent {
- ACCESS, MUTATE;
-
- public boolean isMutate() {
- return this == MUTATE;
- }
+ @Override
+ public ObjectAdapter getObjectAdapter() {
+ return objectAdapter;
}
- Response invokeActionQueryOnly(final String actionId, final JsonRepresentation arguments, Where where) {
- final ObjectAction action = getObjectActionThatIsVisibleForIntent(actionId, Intent.MUTATE, where);
-
- final ActionSemantics.Of actionSemantics = action.getSemantics();
- if (actionSemantics != ActionSemantics.Of.SAFE) {
- throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED, "Method not allowed; action '%s' is not query only", action.getId());
- }
-
- return invokeActionUsingAdapters(action, arguments, SelfLink.INCLUDED);
+ @Override
+ public ObjectAdapterLinkTo getAdapterLinkTo() {
+ return adapterLinkTo;
}
+ //endregion
- Response invokeActionIdempotent(final String actionId, final JsonRepresentation arguments, Where where) {
- final ObjectAction action = getObjectActionThatIsVisibleForIntent(actionId, Intent.MUTATE, where);
-
- final ActionSemantics.Of actionSemantics = action.getSemantics();
- if (!actionSemantics.isIdempotentInNature()) {
- throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED, "Method not allowed; action '%s' is not idempotent", action.getId());
- }
- return invokeActionUsingAdapters(action, arguments, SelfLink.EXCLUDED);
+ public Response objectRepresentation() {
+ return generatorService.objectRepresentation(this);
}
- Response invokeAction(final String actionId, final JsonRepresentation arguments, Where where) {
- final ObjectAction action = getObjectActionThatIsVisibleForIntent(actionId, Intent.MUTATE, where);
-
- return invokeActionUsingAdapters(action, arguments, SelfLink.EXCLUDED);
+ public Response propertyDetails(
+ final String propertyId,
+ final ResponseGeneratorService.MemberMode memberMode,
+ final Caching caching) {
+ return generatorService.propertyDetails(this, propertyId, memberMode, caching);
}
- private Response invokeActionUsingAdapters(final ObjectAction action, final JsonRepresentation arguments, SelfLink selfLink) {
-
- final List<ObjectAdapter> argAdapters = parseAndValidateArguments(action, arguments);
-
- // invoke
- final ObjectAdapter[] argArray2 = argAdapters.toArray(new ObjectAdapter[0]);
- final ObjectAdapter returnedAdapter = action.execute(objectAdapter, argArray2);
-
- // response (void)
- final ActionResultReprRenderer renderer = new ActionResultReprRenderer(resourceContext, null, selfLink, JsonRepresentation.newMap());
-
- renderer.with(new ObjectAndActionInvocation(objectAdapter, action, arguments, returnedAdapter)).using(adapterLinkTo);
+ public Response collectionDetails(
+ final String collectionId,
+ final ResponseGeneratorService.MemberMode memberMode,
+ final Caching caching) {
- final ResponseBuilder respBuilder = ResourceAbstract.responseOfOk(renderer, Caching.NONE);
-
- final Version version = objectAdapter.getVersion();
- ResourceAbstract.addLastModifiedAndETagIfAvailable(respBuilder, version);
-
- return respBuilder.build();
+ return generatorService.collectionDetails(this, collectionId, memberMode, caching);
}
- /**
- *
- * @param resourceContext
- * @param objectSpec
- * - the {@link ObjectSpecification} to interpret the object as.
- * @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 argRepr) {
-
- if (argRepr == null) {
- return null;
- }
-
- if(!argRepr.mapHas("value")) {
- String reason = "No 'value' key";
- argRepr.mapPut("invalidReason", reason);
- throw new IllegalArgumentException(reason);
- }
+ public Response actionPrompt(final String actionId) {
- if (objectSpec == null) {
- String reason = "ObjectSpec is null, cannot validate";
- argRepr.mapPut("invalidReason", reason);
- throw new IllegalArgumentException(reason);
- }
-
- final JsonRepresentation argValueRepr = argRepr.getRepresentation("value");
-
- // value (encodable)
- if (objectSpec.isEncodeable()) {
- try {
- return JsonValueEncoder.asAdapter(objectSpec, argValueRepr, null);
- }catch(IllegalArgumentException ex) {
- argRepr.mapPut("invalidReason", ex.getMessage());
- throw ex;
- }catch(Exception ex) {
- StringBuilder buf = new StringBuilder("Failed to parse representation ");
- try {
- final String reprStr = argRepr.getString("value");
- buf.append("'").append(reprStr).append("' ");
- } catch(Exception ex2) {
- }
- buf.append("as value of type '").append(objectSpec.getShortIdentifier()).append("'");
- String reason = buf.toString();
- argRepr.mapPut("invalidReason", reason);
- throw new IllegalArgumentException(reason);
- }
- }
-
- // reference
- 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(argValueRepr);
- if (oidFromHref == null) {
- 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) {
- final String reason = "'href' does not reference a known entity";
- argRepr.mapPut("invalidReason", reason);
- throw new IllegalArgumentException(reason);
- }
- return objectAdapter;
+ return generatorService.actionPrompt(this, actionId);
}
- /**
- * Similar to
- * {@link #objectAdapterFor(ResourceContext, ObjectSpecification, Object)},
- * however the object being interpreted is a String holding URL encoded JSON
- * (rather than having already been parsed into a Map representation).
- *
- * @throws IOException
- * @throws JsonMappingException
- * @throws JsonParseException
- */
- ObjectAdapter objectAdapterFor(final ObjectSpecification spec, final String urlEncodedJson) throws JsonParseException, JsonMappingException, IOException {
+ public Response invokeActionQueryOnly(final String actionId, final JsonRepresentation arguments) {
- final String json = UrlDecoderUtils.urlDecode(urlEncodedJson);
- final JsonRepresentation representation = JsonMapper.instance().read(json);
- return objectAdapterFor(resourceContext, spec, representation);
+ return generatorService.invokeActionQueryOnly(this, actionId, arguments);
}
+ public Response invokeActionIdempotent(final String actionId, final JsonRepresentation arguments) {
- // ///////////////////////////////////////////////////////////////////
- // get{MemberType}ThatIsVisibleAndUsable
- // ///////////////////////////////////////////////////////////////////
-
- protected OneToOneAssociation getPropertyThatIsVisibleForIntent(final String propertyId, final Intent intent, Where where) {
-
- final ObjectAssociation association;
- try {
- final ObjectSpecification specification = objectAdapter.getSpecification();
- association = specification.getAssociation(propertyId);
- } catch(Exception ex) {
- // fall through
- throwNotFoundException(propertyId, MemberType.PROPERTY);
- return null; // to keep compiler happy.
- }
-
- if (association == null || !association.isOneToOneAssociation()) {
- throwNotFoundException(propertyId, MemberType.PROPERTY);
- }
-
- final OneToOneAssociation property = (OneToOneAssociation) association;
- return memberThatIsVisibleForIntent(property, MemberType.PROPERTY, intent, where);
+ return generatorService.invokeActionIdempotent(this, actionId, arguments);
}
- protected OneToManyAssociation getCollectionThatIsVisibleForIntent(final String collectionId, final Intent intent, Where where) {
+ public Response invokeAction(final String actionId, final JsonRepresentation arguments) {
- final ObjectAssociation association;
- try {
- final ObjectSpecification specification = objectAdapter.getSpecification();
- association = specification.getAssociation(collectionId);
- } catch(Exception ex) {
- // fall through
- throwNotFoundException(collectionId, MemberType.COLLECTION);
- return null; // to keep compiler happy.
- }
- if (association == null || !association.isOneToManyAssociation()) {
- throwNotFoundException(collectionId, MemberType.COLLECTION);
- }
- final OneToManyAssociation collection = (OneToManyAssociation) association;
- return memberThatIsVisibleForIntent(collection, MemberType.COLLECTION, intent, where);
+ return generatorService.invokeAction(this, actionId, arguments);
}
- protected ObjectAction getObjectActionThatIsVisibleForIntent(final String actionId, final Intent intent, Where where) {
-
- final ObjectAction action;
- try {
- final ObjectSpecification specification = objectAdapter.getSpecification();
- action = specification.getObjectAction(actionId);
- } catch(Exception ex) {
- throwNotFoundException(actionId, MemberType.ACTION);
- return null; // to keep compiler happy.
- }
- if (action == null) {
- throwNotFoundException(actionId, MemberType.ACTION);
- }
- return memberThatIsVisibleForIntent(action, MemberType.ACTION, intent, where);
- }
- protected <T extends ObjectMember> T memberThatIsVisibleForIntent(final T objectMember, final MemberType memberType, final Intent intent, Where where) {
- final String memberId = objectMember.getId();
- final AuthenticationSession authenticationSession = resourceContext.getAuthenticationSession();
- if (objectMember.isVisible(authenticationSession, objectAdapter, where).isVetoed()) {
- throwNotFoundException(memberId, memberType);
- }
- if (intent.isMutate()) {
- final Consent usable = objectMember.isUsable(authenticationSession, objectAdapter, where);
- if (usable.isVetoed()) {
- throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.FORBIDDEN, usable.getReason());
- }
- }
- return objectMember;
- }
+ // //////////////////////////////////////
+ // dependencies (from context)
+ // //////////////////////////////////////
- protected static void throwNotFoundException(final String memberId, final MemberType memberType) {
- final String memberTypeStr = memberType.name().toLowerCase();
- throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.NOT_FOUND, "%s '%s' either does not exist or is not visible", memberTypeStr, memberId);
+ private PersistenceSession getPersistenceSession() {
+ return IsisContext.getPersistenceSession();
}
- // ///////////////////////////////////////////////////////////////////
- // parseBody
- // ///////////////////////////////////////////////////////////////////
-
/**
- *
- * @param objectSpec
- * @param bodyAsString
- * - as per {@link #asStringUtf8(InputStream)}
- * @return
+ * Service locator
*/
- ObjectAdapter parseAsMapWithSingleValue(final ObjectSpecification objectSpec, final String bodyAsString) {
- final JsonRepresentation arguments = readAsMap(bodyAsString);
- return parseAsMapWithSingleValue(objectSpec, arguments);
- }
-
- ObjectAdapter parseAsMapWithSingleValue(final ObjectSpecification objectSpec, final JsonRepresentation arguments) {
- final JsonRepresentation representation = arguments.getRepresentation("value");
- if (arguments.size() != 1 || representation == null) {
- 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, arguments);
- }
-
- private List<ObjectAdapter> parseAndValidateArguments(final ObjectAction action, final JsonRepresentation arguments) {
- final List<JsonRepresentation> argList = argListFor(action, arguments);
-
- final List<ObjectAdapter> argAdapters = Lists.newArrayList();
- final List<ObjectActionParameter> parameters = action.getParameters();
- boolean valid = true;
- for (int i = 0; i < argList.size(); i++) {
- final JsonRepresentation argRepr = argList.get(i);
- final ObjectSpecification paramSpec = parameters.get(i).getSpecification();
- try {
- final ObjectAdapter argAdapter = objectAdapterFor(resourceContext, paramSpec, argRepr);
- argAdapters.add(argAdapter);
-
- // validate individual arg
- final ObjectActionParameter parameter = parameters.get(i);
- final Object argPojo = argAdapter!=null?argAdapter.getObject():null;
- final String reasonNotValid = parameter.isValid(objectAdapter, argPojo, null);
- if (reasonNotValid != null) {
- argRepr.mapPut("invalidReason", reasonNotValid);
- valid = false;
- }
- } catch (final IllegalArgumentException e) {
- argAdapters.add(null);
- valid = false;
- }
- }
-
- // validate all args
- final ObjectAdapter[] argArray = argAdapters.toArray(new ObjectAdapter[0]);
- final Consent consent = action.isProposedArgumentSetValid(objectAdapter, argArray);
- if (consent.isVetoed()) {
- arguments.mapPut("x-ro-invalidReason", consent.getReason());
- valid = false;
- }
-
- if(!valid) {
- throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.VALIDATION_FAILED, arguments, "Validation failed, see body for details");
- }
-
- return argAdapters;
- }
-
- private static List<JsonRepresentation> argListFor(final ObjectAction action, final JsonRepresentation arguments) {
- final List<JsonRepresentation> argList = Lists.newArrayList();
-
- // ensure that we have no arguments that are not parameters
- for (final Entry<String, JsonRepresentation> arg : arguments.mapIterable()) {
- final String argName = arg.getKey();
- if (action.getParameterById(argName) == null) {
- String reason = String.format("Argument '%s' found but no such parameter", argName);
- arguments.mapPut("x-ro-invalidReason", reason);
- throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.BAD_REQUEST, arguments, reason);
- }
- }
-
- // ensure that an argument value has been provided for all non-optional
- // parameters
- final List<ObjectActionParameter> parameters = action.getParameters();
- for (final ObjectActionParameter param : parameters) {
- final String paramId = param.getId();
- final JsonRepresentation argRepr = arguments.getRepresentation(paramId);
- if (argRepr == null && !param.isOptional()) {
- String reason = String.format("No argument found for (mandatory) parameter '%s'", paramId);
- arguments.mapPut("x-ro-invalidReason", reason);
- throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.BAD_REQUEST, arguments, reason);
- }
- argList.add(argRepr);
- }
- return argList;
- }
-
- public static JsonRepresentation readParameterMapAsMap(final Map<String, String[]> parameterMap) {
- final JsonRepresentation map = JsonRepresentation.newMap();
- for (final Map.Entry<String, String[]> parameter : parameterMap.entrySet()) {
- map.mapPut(parameter.getKey(), parameter.getValue()[0]);
- }
- return map;
- }
-
- public static JsonRepresentation readQueryStringAsMap(final String queryString) {
- if (queryString == null) {
- return JsonRepresentation.newMap();
- }
- final String queryStringTrimmed = queryString.trim();
- if (queryStringTrimmed.isEmpty()) {
- return JsonRepresentation.newMap();
- }
- return read(queryStringTrimmed, "query string");
- }
-
- public static JsonRepresentation readAsMap(final String body) {
- if (body == null) {
- return JsonRepresentation.newMap();
- }
- final String bodyTrimmed = body.trim();
- if (bodyTrimmed.isEmpty()) {
- return JsonRepresentation.newMap();
- }
- return read(bodyTrimmed, "body");
- }
-
- private static JsonRepresentation read(final String args, final String argsNature) {
- try {
- final JsonRepresentation jsonRepr = JsonMapper.instance().read(args);
- if (!jsonRepr.isMap()) {
- throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "could not read %s as a JSON map", argsNature);
- }
- return jsonRepr;
- } catch (final JsonParseException e) {
- throw RestfulObjectsApplicationException.createWithCauseAndMessage(HttpStatusCode.BAD_REQUEST, e, "could not parse %s", argsNature);
- } catch (final JsonMappingException e) {
- throw RestfulObjectsApplicationException.createWithCauseAndMessage(HttpStatusCode.BAD_REQUEST, e, "could not read %s as JSON", argsNature);
- } catch (final IOException e) {
- throw RestfulObjectsApplicationException.createWithCauseAndMessage(HttpStatusCode.BAD_REQUEST, e, "could not parse %s", argsNature);
- }
- }
-
- public static String asStringUtf8(final InputStream body) {
- try {
- final byte[] byteArray = ByteStreams.toByteArray(body);
- return new String(byteArray, Charsets.UTF_8);
- } catch (final IOException e) {
- throw RestfulObjectsApplicationException.createWithCauseAndMessage(HttpStatusCode.BAD_REQUEST, e, "could not read body");
- }
- }
-
- // //////////////////////////////////////////////////////////////
- // misc
- // //////////////////////////////////////////////////////////////
-
- private static String resourceFor(final ObjectSpecification objectSpec) {
- // TODO: should return a string in the form
- // http://localhost:8080/types/xxx
- return objectSpec.getFullIdentifier();
+ private <T> T lookupService(Class<T> serviceType) {
+ return getPersistenceSession().getServiceOrNull(serviceType);
}
}
+
http://git-wip-us.apache.org/repos/asf/isis/blob/bfece661/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainServiceResourceServerside.java
----------------------------------------------------------------------
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainServiceResourceServerside.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainServiceResourceServerside.java
index 40a16f3..d0ecc93 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainServiceResourceServerside.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainServiceResourceServerside.java
@@ -50,7 +50,7 @@ public class DomainServiceResourceServerside extends ResourceAbstract implements
.includesSelf()
.with(serviceAdapters);
- return responseOfOk(renderer, Caching.ONE_DAY).build();
+ return Responses.ofOk(renderer, Caching.ONE_DAY).build();
}
@Override
@@ -86,7 +86,7 @@ public class DomainServiceResourceServerside extends ResourceAbstract implements
.with(serviceAdapter)
.includesSelf();
- return responseOfOk(renderer, Caching.ONE_DAY).build();
+ return Responses.ofOk(renderer, Caching.ONE_DAY).build();
}
@Override
@@ -119,7 +119,7 @@ public class DomainServiceResourceServerside extends ResourceAbstract implements
final ObjectAdapter serviceAdapter = getServiceAdapter(serviceId);
final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), serviceAdapter).using(new DomainServiceLinkTo());
- return helper.actionPrompt(actionId, getResourceContext().getWhere());
+ return helper.actionPrompt(actionId);
}
@Override
@@ -160,7 +160,7 @@ public class DomainServiceResourceServerside extends ResourceAbstract implements
final ObjectAdapter serviceAdapter = getServiceAdapter(serviceId);
final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), serviceAdapter).using(new DomainServiceLinkTo());
- return helper.invokeActionQueryOnly(actionId, arguments, getResourceContext().getWhere());
+ return helper.invokeActionQueryOnly(actionId, arguments);
}
@@ -181,7 +181,7 @@ public class DomainServiceResourceServerside extends ResourceAbstract implements
final ObjectAdapter serviceAdapter = getServiceAdapter(serviceId);
final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), serviceAdapter).using(new DomainServiceLinkTo());
- return helper.invokeActionIdempotent(actionId, arguments, getResourceContext().getWhere());
+ return helper.invokeActionIdempotent(actionId, arguments);
}
@@ -199,7 +199,7 @@ public class DomainServiceResourceServerside extends ResourceAbstract implements
final ObjectAdapter serviceAdapter = getServiceAdapter(serviceId);
final DomainResourceHelper helper = new DomainResourceHelper(getResourceContext(), serviceAdapter).using(new DomainServiceLinkTo());
- return helper.invokeAction(actionId, arguments, getResourceContext().getWhere());
+ return helper.invokeAction(actionId, arguments);
}
@Override
http://git-wip-us.apache.org/repos/asf/isis/blob/bfece661/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java
----------------------------------------------------------------------
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java
index 6c2c48e..46700c3 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/DomainTypeResourceServerside.java
@@ -81,7 +81,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
final TypeListReprRenderer renderer = new TypeListReprRenderer(getResourceContext(), null, JsonRepresentation.newMap());
renderer.with(allSpecifications).includesSelf();
- return responseOfOk(renderer, Caching.ONE_DAY).build();
+ return Responses.ofOk(renderer, Caching.ONE_DAY).build();
}
@Override
@@ -97,7 +97,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
final DomainTypeReprRenderer renderer = new DomainTypeReprRenderer(getResourceContext(), null, JsonRepresentation.newMap());
renderer.with(objectSpec).includesSelf();
- return responseOfOk(renderer, Caching.ONE_DAY).build();
+ return Responses.ofOk(renderer, Caching.ONE_DAY).build();
}
@Override
@@ -122,7 +122,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
final PropertyDescriptionReprRenderer renderer = new PropertyDescriptionReprRenderer(getResourceContext(), null, JsonRepresentation.newMap());
renderer.with(new ParentSpecAndProperty(parentSpec, property)).includesSelf();
- return responseOfOk(renderer, Caching.ONE_DAY).build();
+ return Responses.ofOk(renderer, Caching.ONE_DAY).build();
}
@Override
@@ -147,7 +147,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
final CollectionDescriptionReprRenderer renderer = new CollectionDescriptionReprRenderer(getResourceContext(), null, JsonRepresentation.newMap());
renderer.with(new ParentSpecAndCollection(parentSpec, collection)).includesSelf();
- return responseOfOk(renderer, Caching.ONE_DAY).build();
+ return Responses.ofOk(renderer, Caching.ONE_DAY).build();
}
@Override
@@ -172,7 +172,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
final ActionDescriptionReprRenderer renderer = new ActionDescriptionReprRenderer(getResourceContext(), null, JsonRepresentation.newMap());
renderer.with(new ParentSpecAndAction(parentSpec, action)).includesSelf();
- return responseOfOk(renderer, Caching.ONE_DAY).build();
+ return Responses.ofOk(renderer, Caching.ONE_DAY).build();
}
@Override
@@ -199,7 +199,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
final ActionParameterDescriptionReprRenderer renderer = new ActionParameterDescriptionReprRenderer(getResourceContext(), null, JsonRepresentation.newMap());
renderer.with(new ParentSpecAndActionParam(parentSpec, actionParam)).includesSelf();
- return responseOfOk(renderer, Caching.ONE_DAY).build();
+ return Responses.ofOk(renderer, Caching.ONE_DAY).build();
}
// //////////////////////////////////////////////////////////
@@ -232,7 +232,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
final boolean value = domainTypeSpec.isOfType(supertypeSpec);
renderer.with(domainTypeSpec).withSelf(selfLink).withValue(value);
- return responseOfOk(renderer, Caching.ONE_DAY).build();
+ return Responses.ofOk(renderer, Caching.ONE_DAY).build();
}
@@ -263,7 +263,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
final boolean value = subtypeSpec.isOfType(domainTypeSpec);
renderer.with(domainTypeSpec).withSelf(selfLink).withValue(value);
- return responseOfOk(renderer, Caching.ONE_DAY).build();
+ return Responses.ofOk(renderer, Caching.ONE_DAY).build();
}
private static String domainTypeFor(
@@ -284,7 +284,7 @@ public class DomainTypeResourceServerside extends ResourceAbstract implements Do
}
private static String linkFromFormalArgs(final String argumentsAsQueryString, final String paramName) {
- final JsonRepresentation arguments = DomainResourceHelper.readQueryStringAsMap(argumentsAsQueryString);
+ final JsonRepresentation arguments = Util.readQueryStringAsMap(argumentsAsQueryString);
if (!arguments.isLink(paramName)) {
throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Args should contain a link '%s'", paramName);
}
http://git-wip-us.apache.org/repos/asf/isis/blob/bfece661/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/HomePageResourceServerside.java
----------------------------------------------------------------------
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/HomePageResourceServerside.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/HomePageResourceServerside.java
index b340730..72fef2b 100644
--- a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/HomePageResourceServerside.java
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/HomePageResourceServerside.java
@@ -43,7 +43,7 @@ public class HomePageResourceServerside extends ResourceAbstract implements Home
final HomePageReprRenderer renderer = new HomePageReprRenderer(getResourceContext(), null, JsonRepresentation.newMap());
renderer.includesSelf();
- return responseOfOk(renderer, Caching.ONE_DAY).build();
+ return Responses.ofOk(renderer, Caching.ONE_DAY).build();
}
@Override
http://git-wip-us.apache.org/repos/asf/isis/blob/bfece661/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/JsonParserHelper.java
----------------------------------------------------------------------
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/JsonParserHelper.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/JsonParserHelper.java
new file mode 100644
index 0000000..97cce01
--- /dev/null
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/JsonParserHelper.java
@@ -0,0 +1,141 @@
+/**
+ * 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.server.resources;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
+import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse;
+import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.JsonValueEncoder;
+import org.apache.isis.viewer.restfulobjects.server.ResourceContext;
+import org.apache.isis.viewer.restfulobjects.server.RestfulObjectsApplicationException;
+import org.apache.isis.viewer.restfulobjects.server.util.OidUtils;
+import org.apache.isis.viewer.restfulobjects.server.util.UrlParserUtils;
+
+/**
+ * Utility class that encapsulates the logic for parsing some content (JSON, or a simple string that is JSON)
+ * into an{@link org.apache.isis.core.metamodel.adapter.ObjectAdapter} of a specified
+ * {@link org.apache.isis.core.metamodel.spec.ObjectSpecification type}.
+ */
+public class JsonParserHelper {
+
+ static enum Intent {
+ ACCESS, MUTATE;
+
+ public boolean isMutate() {
+ return this == MUTATE;
+ }
+ }
+
+ private final ResourceContext resourceContext;
+ private final ObjectSpecification objectSpec;
+
+ public JsonParserHelper(ResourceContext resourceContext, ObjectSpecification objectSpecification) {
+ this.objectSpec = objectSpecification;
+ this.resourceContext = resourceContext;
+ }
+
+
+ /**
+ * @param bodyAsString
+ * - as per {@link Util#asStringUtf8(java.io.InputStream)}
+ * @return
+ */
+ ObjectAdapter parseAsMapWithSingleValue(final String bodyAsString) {
+ final JsonRepresentation arguments = Util.readAsMap(bodyAsString);
+ return parseAsMapWithSingleValue(arguments);
+ }
+
+ ObjectAdapter parseAsMapWithSingleValue(final JsonRepresentation arguments) {
+ final JsonRepresentation representation = arguments.getRepresentation("value");
+ if (arguments.size() != 1 || representation == null) {
+ throw RestfulObjectsApplicationException.createWithMessage(RestfulResponse.HttpStatusCode.BAD_REQUEST, "Body should be a map with a single key 'value' whose value represents an instance of type '%s'", Util.resourceFor(objectSpec));
+ }
+
+ return objectAdapterFor(arguments);
+ }
+
+ /**
+ *
+ * @param argRepr
+ * - expected to be either a String or a Map (ie from within a
+ * List, built by parsing a JSON structure).
+ */
+ public ObjectAdapter objectAdapterFor(final JsonRepresentation argRepr) {
+
+ if (argRepr == null) {
+ return null;
+ }
+
+ if(!argRepr.mapHas("value")) {
+ String reason = "No 'value' key";
+ argRepr.mapPut("invalidReason", reason);
+ throw new IllegalArgumentException(reason);
+ }
+
+ if (objectSpec == null) {
+ String reason = "ObjectSpec is null, cannot validate";
+ argRepr.mapPut("invalidReason", reason);
+ throw new IllegalArgumentException(reason);
+ }
+
+ final JsonRepresentation argValueRepr = argRepr.getRepresentation("value");
+
+ // value (encodable)
+ if (objectSpec.isEncodeable()) {
+ try {
+ return JsonValueEncoder.asAdapter(objectSpec, argValueRepr, null);
+ }catch(IllegalArgumentException ex) {
+ argRepr.mapPut("invalidReason", ex.getMessage());
+ throw ex;
+ }catch(Exception ex) {
+ StringBuilder buf = new StringBuilder("Failed to parse representation ");
+ try {
+ final String reprStr = argRepr.getString("value");
+ buf.append("'").append(reprStr).append("' ");
+ } catch(Exception ex2) {
+ }
+ buf.append("as value of type '").append(objectSpec.getShortIdentifier()).append("'");
+ String reason = buf.toString();
+ argRepr.mapPut("invalidReason", reason);
+ throw new IllegalArgumentException(reason);
+ }
+ }
+
+ // reference
+ 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(argValueRepr);
+ if (oidFromHref == null) {
+ 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) {
+ final String reason = "'href' does not reference a known entity";
+ argRepr.mapPut("invalidReason", reason);
+ throw new IllegalArgumentException(reason);
+ }
+ return objectAdapter;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/bfece661/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/ObjectActionArgHelper.java
----------------------------------------------------------------------
diff --git a/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/ObjectActionArgHelper.java b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/ObjectActionArgHelper.java
new file mode 100644
index 0000000..1d807a5
--- /dev/null
+++ b/core/viewer-restfulobjects-server/src/main/java/org/apache/isis/viewer/restfulobjects/server/resources/ObjectActionArgHelper.java
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.viewer.restfulobjects.server.resources;
+
+import java.util.List;
+import java.util.Map;
+import com.google.common.collect.Lists;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
+import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
+import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse;
+import org.apache.isis.viewer.restfulobjects.server.ResourceContext;
+import org.apache.isis.viewer.restfulobjects.server.RestfulObjectsApplicationException;
+
+/**
+ * Utility class that encapsulates the logic for parsing arguments to be invoked by an
+ * {@link org.apache.isis.core.metamodel.spec.feature.ObjectAction}.
+ */
+public class ObjectActionArgHelper {
+
+ private final ResourceContext resourceContext;
+ private final ObjectAdapter objectAdapter;
+ private final ObjectAction action;
+
+ public ObjectActionArgHelper(
+ final ResourceContext resourceContext,
+ final ObjectAdapter objectAdapter,
+ final ObjectAction action) {
+ this.resourceContext = resourceContext;
+ this.objectAdapter = objectAdapter;
+ this.action = action;
+ }
+
+ public List<ObjectAdapter> parseAndValidateArguments(final JsonRepresentation arguments) {
+ final List<JsonRepresentation> argList = argListFor(action, arguments);
+
+ final List<ObjectAdapter> argAdapters = Lists.newArrayList();
+ final List<ObjectActionParameter> parameters = action.getParameters();
+ boolean valid = true;
+ for (int i = 0; i < argList.size(); i++) {
+ final JsonRepresentation argRepr = argList.get(i);
+ final ObjectSpecification paramSpec = parameters.get(i).getSpecification();
+ try {
+ final ObjectAdapter argAdapter = new JsonParserHelper(resourceContext, paramSpec).objectAdapterFor(argRepr);
+ argAdapters.add(argAdapter);
+
+ // validate individual arg
+ final ObjectActionParameter parameter = parameters.get(i);
+ final Object argPojo = argAdapter!=null?argAdapter.getObject():null;
+ final String reasonNotValid = parameter.isValid(objectAdapter, argPojo, null);
+ if (reasonNotValid != null) {
+ argRepr.mapPut("invalidReason", reasonNotValid);
+ valid = false;
+ }
+ } catch (final IllegalArgumentException e) {
+ argAdapters.add(null);
+ valid = false;
+ }
+ }
+
+ // validate all args
+ final ObjectAdapter[] argArray = argAdapters.toArray(new ObjectAdapter[0]);
+ final Consent consent = action.isProposedArgumentSetValid(objectAdapter, argArray);
+ if (consent.isVetoed()) {
+ arguments.mapPut("x-ro-invalidReason", consent.getReason());
+ valid = false;
+ }
+
+ if(!valid) {
+ throw RestfulObjectsApplicationException.createWithBody(RestfulResponse.HttpStatusCode.VALIDATION_FAILED, arguments, "Validation failed, see body for details");
+ }
+
+ return argAdapters;
+ }
+
+ private static List<JsonRepresentation> argListFor(final ObjectAction action, final JsonRepresentation arguments) {
+ final List<JsonRepresentation> argList = Lists.newArrayList();
+
+ // ensure that we have no arguments that are not parameters
+ for (final Map.Entry<String, JsonRepresentation> arg : arguments.mapIterable()) {
+ final String argName = arg.getKey();
+ if (action.getParameterById(argName) == null) {
+ String reason = String.format("Argument '%s' found but no such parameter", argName);
+ arguments.mapPut("x-ro-invalidReason", reason);
+ throw RestfulObjectsApplicationException.createWithBody(RestfulResponse.HttpStatusCode.BAD_REQUEST, arguments, reason);
+ }
+ }
+
+ // ensure that an argument value has been provided for all non-optional
+ // parameters
+ final List<ObjectActionParameter> parameters = action.getParameters();
+ for (final ObjectActionParameter param : parameters) {
+ final String paramId = param.getId();
+ final JsonRepresentation argRepr = arguments.getRepresentation(paramId);
+ if (argRepr == null && !param.isOptional()) {
+ String reason = String.format("No argument found for (mandatory) parameter '%s'", paramId);
+ arguments.mapPut("x-ro-invalidReason", reason);
+ throw RestfulObjectsApplicationException.createWithBody(RestfulResponse.HttpStatusCode.BAD_REQUEST, arguments, reason);
+ }
+ argList.add(argRepr);
+ }
+ return argList;
+ }
+
+}