/** * 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.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.core.MediaType; import javax.ws.rs.core.Response; import org.apache.isis.applib.annotation.Where; import org.apache.isis.applib.services.command.Command; import org.apache.isis.core.commons.url.UrlEncodingUtils; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; import org.apache.isis.core.metamodel.consent.Consent; import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy; import org.apache.isis.core.metamodel.spec.ObjectSpecId; import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; import org.apache.isis.viewer.restfulobjects.applib.RepresentationType; import org.apache.isis.viewer.restfulobjects.applib.RestfulMediaType; 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.rendering.RestfulObjectsApplicationException; import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.MemberReprMode; import org.apache.isis.viewer.restfulobjects.rendering.service.RepresentationService; import org.apache.isis.viewer.restfulobjects.rendering.service.conneg.PrettyPrinting; import org.apache.isis.viewer.restfulobjects.rendering.util.Util; @Path("/objects") public class DomainObjectResourceServerside extends ResourceAbstract implements DomainObjectResource { // ////////////////////////////////////////////////////////// // persist // ////////////////////////////////////////////////////////// @Override @POST @Path("/{domainType}") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_OBJECT, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_OBJECT, RestfulMediaType.APPLICATION_XML_ERROR }) public Response persist(@PathParam("domainType") String domainType, final InputStream object) { init(RepresentationType.DOMAIN_OBJECT, Where.OBJECT_FORMS, RepresentationService.Intent.JUST_CREATED); 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); } final ObjectSpecification domainTypeSpec = getSpecificationLoader().lookupBySpecId(ObjectSpecId.of(domainType)); if (domainTypeSpec == null) { 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 ObjectAdapterUpdateHelper updateHelper = new ObjectAdapterUpdateHelper(getResourceContext(), objectAdapter); final JsonRepresentation membersMap = objectRepr.getMap("members"); if (membersMap == null) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.BAD_REQUEST, "Could not find members map; got %s", objectRepr); } if (!updateHelper.copyOverProperties(membersMap, ObjectAdapterUpdateHelper.Intent.PERSISTING_NEW)) { throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.BAD_REQUEST, objectRepr, "Illegal property value"); } final Consent validity = objectAdapter.getSpecification().isValid(objectAdapter, InteractionInitiatedBy.USER); if (validity.isVetoed()) { throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.BAD_REQUEST, objectRepr, validity.getReason()); } getResourceContext().getPersistenceSession().makePersistentInTransaction(objectAdapter); return newDomainResourceHelper(objectAdapter).objectRepresentation(RepresentationService.Intent.JUST_CREATED); } // ////////////////////////////////////////////////////////// // domain object // ////////////////////////////////////////////////////////// @Override @GET @Path("/{domainType}/{instanceId}") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_OBJECT, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_OBJECT, RestfulMediaType.APPLICATION_XML_ERROR }) @PrettyPrinting public Response object(@PathParam("domainType") String domainType, @PathParam("instanceId") final String instanceId) { init(RepresentationType.DOMAIN_OBJECT, Where.OBJECT_FORMS, RepresentationService.Intent.ALREADY_PERSISTENT); final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); return newDomainResourceHelper(objectAdapter).objectRepresentation(); } @Override @PUT @Path("/{domainType}/{instanceId}") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_OBJECT, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_OBJECT, RestfulMediaType.APPLICATION_XML_ERROR }) @PrettyPrinting public Response object(@PathParam("domainType") String domainType, @PathParam("instanceId") final String instanceId, final InputStream object) { init(RepresentationType.DOMAIN_OBJECT, Where.OBJECT_FORMS, RepresentationService.Intent.ALREADY_PERSISTENT); 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 (!updateHelper.copyOverProperties(argRepr, ObjectAdapterUpdateHelper.Intent.UPDATE_EXISTING)) { throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.BAD_REQUEST, argRepr, "Illegal property value"); } final Consent validity = objectAdapter.getSpecification().isValid(objectAdapter, InteractionInitiatedBy.USER); if (validity.isVetoed()) { throw RestfulObjectsApplicationException.createWithBody(HttpStatusCode.BAD_REQUEST, argRepr, validity.getReason()); } return newDomainResourceHelper(objectAdapter).objectRepresentation(); } @Override public Response deleteMethodNotSupported(@PathParam("domainType") String domainType, @PathParam("instanceId") String instanceId) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED, "Deleting objects is not supported."); } @Override public Response postMethodNotAllowed(@PathParam("domainType") String domainType, @PathParam("instanceId") String instanceId) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED, "Posting to object resource is not allowed."); } // ////////////////////////////////////////////////////////// // domain object property // ////////////////////////////////////////////////////////// @Override @GET @Path("/{domainType}/{instanceId}/properties/{propertyId}") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_OBJECT_PROPERTY, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_OBJECT_PROPERTY, RestfulMediaType.APPLICATION_XML_ERROR }) @PrettyPrinting public Response propertyDetails(@PathParam("domainType") String domainType, @PathParam("instanceId") final String instanceId, @PathParam("propertyId") final String propertyId) { init(RepresentationType.OBJECT_PROPERTY, Where.OBJECT_FORMS, RepresentationService.Intent.NOT_APPLICABLE); final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); final DomainResourceHelper helper = newDomainResourceHelper(objectAdapter); return helper.propertyDetails( propertyId, MemberReprMode.READ ); } @Override @PUT @Path("/{domainType}/{instanceId}/properties/{propertyId}") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_OBJECT_PROPERTY, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_OBJECT_PROPERTY, RestfulMediaType.APPLICATION_XML_ERROR }) public Response modifyProperty(@PathParam("domainType") String domainType, @PathParam("instanceId") final String instanceId, @PathParam("propertyId") final String propertyId, final InputStream body) { init(Where.OBJECT_FORMS, RepresentationService.Intent.NOT_APPLICABLE); setCommandExecutor(Command.Executor.USER); final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); final DomainResourceHelper helper = newDomainResourceHelper(objectAdapter); final ObjectAdapterAccessHelper accessHelper = new ObjectAdapterAccessHelper(getResourceContext(), objectAdapter); final OneToOneAssociation property = accessHelper.getPropertyThatIsVisibleForIntent(propertyId, ObjectAdapterAccessHelper.Intent.MUTATE); final ObjectSpecification propertySpec = property.getSpecification(); final String bodyAsString = Util.asStringUtf8(body); final ObjectAdapter argAdapter = new JsonParserHelper(getResourceContext(), propertySpec).parseAsMapWithSingleValue( bodyAsString); final Consent consent = property.isAssociationValid(objectAdapter, argAdapter, InteractionInitiatedBy.USER); if (consent.isVetoed()) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.UNAUTHORIZED, consent.getReason()); } property.set(objectAdapter, argAdapter, InteractionInitiatedBy.USER); return helper.propertyDetails( propertyId, MemberReprMode.WRITE ); } @Override @DELETE @Path("/{domainType}/{instanceId}/properties/{propertyId}") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_OBJECT_PROPERTY, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_OBJECT_PROPERTY, RestfulMediaType.APPLICATION_XML_ERROR }) public Response clearProperty(@PathParam("domainType") String domainType, @PathParam("instanceId") final String instanceId, @PathParam("propertyId") final String propertyId) { init(Where.OBJECT_FORMS, RepresentationService.Intent.NOT_APPLICABLE); setCommandExecutor(Command.Executor.USER); final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); final DomainResourceHelper helper = newDomainResourceHelper(objectAdapter); final ObjectAdapterAccessHelper accessHelper = new ObjectAdapterAccessHelper(getResourceContext(), objectAdapter); final OneToOneAssociation property = accessHelper.getPropertyThatIsVisibleForIntent( propertyId, ObjectAdapterAccessHelper.Intent.MUTATE); final Consent consent = property.isAssociationValid(objectAdapter, null, InteractionInitiatedBy.USER); if (consent.isVetoed()) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.UNAUTHORIZED, consent.getReason()); } property.set(objectAdapter, null, InteractionInitiatedBy.USER); return helper.propertyDetails( propertyId, MemberReprMode.WRITE ); } @Override public Response postPropertyNotAllowed(@PathParam("domainType") String domainType, @PathParam("instanceId") String instanceId, @PathParam("propertyId") String propertyId) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED, "Posting to a property resource is not allowed."); } // ////////////////////////////////////////////////////////// // domain object collection // ////////////////////////////////////////////////////////// @Override @GET @Path("/{domainType}/{instanceId}/collections/{collectionId}") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_OBJECT_COLLECTION, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_OBJECT_COLLECTION, RestfulMediaType.APPLICATION_XML_ERROR }) @PrettyPrinting public Response accessCollection(@PathParam("domainType") String domainType, @PathParam("instanceId") final String instanceId, @PathParam("collectionId") final String collectionId) { init(RepresentationType.OBJECT_COLLECTION, Where.PARENTED_TABLES, RepresentationService.Intent.NOT_APPLICABLE); final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); return newDomainResourceHelper(objectAdapter).collectionDetails(collectionId, MemberReprMode.READ); } @Override @PUT @Path("/{domainType}/{instanceId}/collections/{collectionId}") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_OBJECT_COLLECTION, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_OBJECT_COLLECTION, RestfulMediaType.APPLICATION_XML_ERROR }) public Response addToSet(@PathParam("domainType") String domainType, @PathParam("instanceId") final String instanceId, @PathParam("collectionId") final String collectionId, final InputStream body) { init(Where.PARENTED_TABLES, RepresentationService.Intent.NOT_APPLICABLE); final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); final DomainResourceHelper helper = newDomainResourceHelper(objectAdapter); final ObjectAdapterAccessHelper accessHelper = new ObjectAdapterAccessHelper(getResourceContext(), objectAdapter); 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 = Util.asStringUtf8(body); final ObjectAdapter argAdapter = new JsonParserHelper(getResourceContext(), collectionSpec).parseAsMapWithSingleValue( bodyAsString); final Consent consent = collection.isValidToAdd(objectAdapter, argAdapter, InteractionInitiatedBy.USER); if (consent.isVetoed()) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.UNAUTHORIZED, consent.getReason()); } collection.addElement(objectAdapter, argAdapter, InteractionInitiatedBy.USER); return helper.collectionDetails(collectionId, MemberReprMode.WRITE); } @Override @POST @Path("/{domainType}/{instanceId}/collections/{collectionId}") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_OBJECT_COLLECTION, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_OBJECT_COLLECTION, RestfulMediaType.APPLICATION_XML_ERROR }) public Response addToList(@PathParam("domainType") String domainType, @PathParam("instanceId") final String instanceId, @PathParam("collectionId") final String collectionId, final InputStream body) { init(Where.PARENTED_TABLES, RepresentationService.Intent.NOT_APPLICABLE); final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); final DomainResourceHelper helper = newDomainResourceHelper(objectAdapter); final ObjectAdapterAccessHelper accessHelper = new ObjectAdapterAccessHelper(getResourceContext(), objectAdapter); 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 = Util.asStringUtf8(body); final ObjectAdapter argAdapter = new JsonParserHelper(getResourceContext(), collectionSpec).parseAsMapWithSingleValue( bodyAsString); final Consent consent = collection.isValidToAdd(objectAdapter, argAdapter, InteractionInitiatedBy.USER); if (consent.isVetoed()) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.UNAUTHORIZED, consent.getReason()); } collection.addElement(objectAdapter, argAdapter, InteractionInitiatedBy.USER); return helper.collectionDetails(collectionId, MemberReprMode.WRITE); } @Override @DELETE @Path("/{domainType}/{instanceId}/collections/{collectionId}") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_OBJECT_COLLECTION, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_OBJECT_COLLECTION, RestfulMediaType.APPLICATION_XML_ERROR }) public Response removeFromCollection(@PathParam("domainType") String domainType, @PathParam("instanceId") final String instanceId, @PathParam("collectionId") final String collectionId) { init(Where.PARENTED_TABLES, RepresentationService.Intent.NOT_APPLICABLE); final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); final DomainResourceHelper helper = newDomainResourceHelper(objectAdapter); final ObjectAdapterAccessHelper accessHelper = new ObjectAdapterAccessHelper(getResourceContext(), objectAdapter); final OneToManyAssociation collection = accessHelper.getCollectionThatIsVisibleForIntent( collectionId, ObjectAdapterAccessHelper.Intent.MUTATE); final ObjectSpecification collectionSpec = collection.getSpecification(); final ObjectAdapter argAdapter = new JsonParserHelper(getResourceContext(), collectionSpec).parseAsMapWithSingleValue( getResourceContext().getUrlUnencodedQueryString()); final Consent consent = collection.isValidToRemove(objectAdapter, argAdapter, InteractionInitiatedBy.USER); if (consent.isVetoed()) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.UNAUTHORIZED, consent.getReason()); } collection.removeElement(objectAdapter, argAdapter, InteractionInitiatedBy.USER); return helper.collectionDetails(collectionId, MemberReprMode.WRITE); } // ////////////////////////////////////////////////////////// // domain object action // ////////////////////////////////////////////////////////// @Override @GET @Path("/{domainType}/{instanceId}/actions/{actionId}") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_OBJECT_ACTION, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_OBJECT_ACTION, RestfulMediaType.APPLICATION_XML_ERROR }) @PrettyPrinting public Response actionPrompt(@PathParam("domainType") String domainType, @PathParam("instanceId") final String instanceId, @PathParam("actionId") final String actionId) { init(RepresentationType.OBJECT_ACTION, Where.OBJECT_FORMS, RepresentationService.Intent.NOT_APPLICABLE); final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); final DomainResourceHelper helper = newDomainResourceHelper(objectAdapter); return helper.actionPrompt(actionId); } @Override public Response deleteActionPromptNotAllowed(@PathParam("domainType") String domainType, @PathParam("instanceId") String instanceId, @PathParam("actionId") String actionId) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED, "Deleting action prompt resource is not allowed."); } @Override public Response postActionPromptNotAllowed(@PathParam("domainType") String domainType, @PathParam("instanceId") String instanceId, @PathParam("actionId") String actionId) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED, "Posting to an action prompt resource is not allowed."); } @Override public Response putActionPromptNotAllowed(@PathParam("domainType") String domainType, @PathParam("instanceId") String instanceId, @PathParam("actionId") String actionId) { throw RestfulObjectsApplicationException.createWithMessage(HttpStatusCode.METHOD_NOT_ALLOWED, "Putting to an action prompt resource is not allowed."); } // ////////////////////////////////////////////////////////// // domain object action invoke // ////////////////////////////////////////////////////////// @Override @GET @Path("/{domainType}/{instanceId}/actions/{actionId}/invoke") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_ACTION_RESULT, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_ACTION_RESULT, RestfulMediaType.APPLICATION_XML_ERROR }) @PrettyPrinting public Response invokeActionQueryOnly( final @PathParam("domainType") String domainType, final @PathParam("instanceId") String instanceId, final @PathParam("actionId") String actionId, final @QueryParam("x-isis-querystring") String xIsisUrlEncodedQueryString) { final String urlUnencodedQueryString = UrlEncodingUtils.urlDecodeNullSafe(xIsisUrlEncodedQueryString != null? xIsisUrlEncodedQueryString: httpServletRequest.getQueryString()); init(RepresentationType.ACTION_RESULT, Where.STANDALONE_TABLES, RepresentationService.Intent.NOT_APPLICABLE, urlUnencodedQueryString); setCommandExecutor(Command.Executor.USER); final JsonRepresentation arguments = getResourceContext().getQueryStringAsJsonRepr(); final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); final DomainResourceHelper helper = newDomainResourceHelper(objectAdapter); return helper.invokeActionQueryOnly(actionId, arguments); } @Override @PUT @Path("/{domainType}/{instanceId}/actions/{actionId}/invoke") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_ACTION_RESULT, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_ACTION_RESULT, RestfulMediaType.APPLICATION_XML_ERROR }) @PrettyPrinting public Response invokeActionIdempotent( final @PathParam("domainType") String domainType, final @PathParam("instanceId") String instanceId, final @PathParam("actionId") String actionId, final InputStream body) { init(RepresentationType.ACTION_RESULT, Where.STANDALONE_TABLES, RepresentationService.Intent.NOT_APPLICABLE, body); setCommandExecutor(Command.Executor.USER); final JsonRepresentation arguments = getResourceContext().getQueryStringAsJsonRepr(); final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); final DomainResourceHelper helper = newDomainResourceHelper(objectAdapter); return helper.invokeActionIdempotent(actionId, arguments); } @Override @POST @Path("/{domainType}/{instanceId}/actions/{actionId}/invoke") @Consumes({ MediaType.WILDCARD }) @Produces({ MediaType.APPLICATION_JSON, RestfulMediaType.APPLICATION_JSON_ACTION_RESULT, RestfulMediaType.APPLICATION_JSON_ERROR, MediaType.APPLICATION_XML, RestfulMediaType.APPLICATION_XML_ACTION_RESULT, RestfulMediaType.APPLICATION_XML_ERROR }) @PrettyPrinting public Response invokeAction( @PathParam("domainType") String domainType, @PathParam("instanceId") final String instanceId, @PathParam("actionId") final String actionId, final InputStream body) { init(RepresentationType.ACTION_RESULT, Where.STANDALONE_TABLES, RepresentationService.Intent.NOT_APPLICABLE, body); setCommandExecutor(Command.Executor.USER); final JsonRepresentation arguments = getResourceContext().getQueryStringAsJsonRepr(); final ObjectAdapter objectAdapter = getObjectAdapterElseThrowNotFound(domainType, instanceId); final DomainResourceHelper helper = newDomainResourceHelper(objectAdapter); return helper.invokeAction(actionId, arguments); } @Override public Response deleteInvokeActionNotAllowed(@PathParam("domainType") String domainType, @PathParam("instanceId") String instanceId, @PathParam("actionId") String actionId) { throw RestfulObjectsApplicationException.createWithMessage(RestfulResponse.HttpStatusCode.METHOD_NOT_ALLOWED, "Deleting an action invocation resource is not allowed."); } private DomainResourceHelper newDomainResourceHelper(final ObjectAdapter objectAdapter) { return new DomainResourceHelper(getResourceContext(), objectAdapter); } }