提交 457df099 编写于 作者: F Frederik Heremans

Added documentation + MediaTypeMapper + RestResponseFactory

上级 c71f6154
......@@ -24,6 +24,7 @@ import java.util.Map;
import org.activiti.engine.identity.Group;
import org.activiti.rest.application.ActivitiRestApplication;
import org.codehaus.jackson.JsonNode;
import org.restlet.data.MediaType;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.resource.ServerResource;
......@@ -54,6 +55,22 @@ public class SecuredResource extends ServerResource {
return url.toString();
}
/**
* @return the {@link MediaType} resolved from the given resource-name. Returns null if the type cannot
* be resolved based on the given name.
*/
public MediaType resolveMediaType(String resourceName) {
return ((ActivitiRestApplication)getApplication()).getMediaTypeResolver().resolveMediaType(resourceName);
}
/**
* @return the Restlet Application, casted to the given type.
*/
@SuppressWarnings("unchecked")
public <T extends ActivitiRestApplication> T getApplication(Class<T> applicationClass) {
return (T) getApplication();
}
/**
* Get a request attribute value, decoded.
*/
......@@ -103,6 +120,7 @@ public class SecuredResource extends ServerResource {
}
}
@SuppressWarnings("deprecation")
protected Map<String, Object> retrieveVariables(JsonNode jsonNode) {
Map<String, Object> variables = new HashMap<String, Object>();
if (jsonNode != null) {
......@@ -121,6 +139,8 @@ public class SecuredResource extends ServerResource {
} else if (valueNode.isTextual()) {
variables.put(name, valueNode.getTextValue());
} else {
// Not using asText() due to the fact we expect a null-value to be returned rather than en emtpy string
// when node is not a simple value-node
variables.put(name, valueNode.getValueAsText());
}
}
......
......@@ -29,12 +29,25 @@ public abstract class ActivitiRestApplication extends Application {
protected ChallengeAuthenticator authenticator;
protected ActivitiStatusService activitiStatusService;
protected MediaTypeResolver mediaTypeResolver;
public ActivitiRestApplication() {
activitiStatusService = new ActivitiStatusService();
}
public MediaTypeResolver getMediaTypeResolver() {
if(mediaTypeResolver == null) {
// Revert to default implementation when no custom resolver has been set
mediaTypeResolver = new DefaultMediaTypeResolver();
}
return mediaTypeResolver;
}
public void setMediaTypeResolver(MediaTypeResolver mediaTypeResolver) {
this.mediaTypeResolver = mediaTypeResolver;
}
public void initializeAuthentication() {
Verifier verifier = new SecretVerifier() {
......
/* Licensed 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.activiti.rest.application;
import org.restlet.data.MediaType;
/**
* Default implementation of a {@link MediaTypeResolver}, resolving a limited set
* of well-known media-types used in the engine.
*
* @author Frederik Heremans
*/
public class DefaultMediaTypeResolver implements MediaTypeResolver {
@Override
public MediaType resolveMediaType(String resourceName) {
MediaType mediaType = null;
if(resourceName != null && !resourceName.isEmpty()) {
String lowerResourceName = resourceName.toLowerCase();
if (lowerResourceName.endsWith("png")) {
mediaType = MediaType.IMAGE_PNG;
} else if (lowerResourceName.endsWith("xml") || lowerResourceName.endsWith("bpmn")) {
mediaType = MediaType.TEXT_XML;
}
}
return mediaType;
}
}
/* Licensed 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.activiti.rest.application;
import org.restlet.data.MediaType;
/**
* Interface describing a class that is capable of resolving the media-type of a resource/file
* based on the resource name.
*
* @author Frederik Heremans
*/
public interface MediaTypeResolver {
/**
* @return the {@link MediaType} resolved from the given resourcename. Returns null if the
* media-type cannot be resolved.
*/
MediaType resolveMediaType(String resourceName);
}
/* Licensed 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.activiti.rest.api;
import org.activiti.engine.impl.bpmn.deployer.BpmnDeployer;
import org.activiti.engine.repository.Deployment;
import org.activiti.rest.api.repository.DeploymentResourceResponse;
import org.activiti.rest.api.repository.DeploymentResourceResponse.DeploymentResourceType;
import org.activiti.rest.api.repository.DeploymentResponse;
import org.restlet.data.MediaType;
/**
* Default implementation of a {@link RestResponseFactory}.
*
* @author Frederik Heremans
*/
public class DefaultRestResponseFactory implements RestResponseFactory {
@Override
public DeploymentResponse createDeploymentResponse(SecuredResource resourceContext, Deployment deployment) {
return new DeploymentResponse(deployment, resourceContext.createFullResourceUrl(RestUrls.URL_DEPLOYMENT, deployment.getId()));
}
@Override
public DeploymentResourceResponse createDeploymentResourceResponse(SecuredResource resourceContext, String deploymentId, String resourceId) {
// Create URL's
String resourceUrl = resourceContext.createFullResourceUrl(RestUrls.URL_DEPLOYMENT_RESOURCE, deploymentId, resourceId);
String resourceContentUrl = resourceContext.createFullResourceUrl(RestUrls.URL_DEPLOYMENT_RESOURCE_CONTENT, deploymentId, resourceId);
// Fetch media-type
MediaType mediaType = resourceContext.resolveMediaType(resourceId);
String mediaTypeString = (mediaType != null) ? mediaType.toString() : null;
// Determine type
// TODO: do based on the returned resource-POJO from the API once ready instead of doing it here
DeploymentResourceType type = DeploymentResourceType.RESOURCE;
for(String suffix : BpmnDeployer.BPMN_RESOURCE_SUFFIXES) {
if(resourceId.endsWith(suffix)) {
type = DeploymentResourceType.PROCESS_DEFINITION;
break;
}
}
return new DeploymentResourceResponse(resourceId, resourceUrl, resourceContentUrl, mediaTypeString, type);
}
}
/* Licensed 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.activiti.rest.api;
import org.activiti.engine.repository.Deployment;
import org.activiti.rest.api.repository.DeploymentResourceResponse;
import org.activiti.rest.api.repository.DeploymentResponse;
/**
* Interface that describes a factory class that is capable of creating response objects
* that can be used to return as a result (or part of a result) from a REST-service call.
* <br>
* All methods require a {@link SecuredResource}, which is used to create URL in right context and provides
* access to shared logic and the application.
*
* @author Frederik Heremans
*/
public interface RestResponseFactory {
DeploymentResponse createDeploymentResponse(SecuredResource resourceContext, Deployment deployment);
DeploymentResourceResponse createDeploymentResourceResponse(SecuredResource resourceContext, String deploymentId, String resourceId);
}
......@@ -27,10 +27,11 @@ import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.DeploymentQuery;
import org.activiti.rest.api.ActivitiUtil;
import org.activiti.rest.api.DataResponse;
import org.activiti.rest.api.RestUrls;
import org.activiti.rest.api.SecuredResource;
import org.activiti.rest.application.ActivitiRestServicesApplication;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.ext.fileupload.RestletFileUpload;
import org.restlet.representation.Representation;
......@@ -87,6 +88,10 @@ public class DeploymentCollectionResource extends SecuredResource {
public DeploymentResponse uploadDeployment(Representation entity) {
try {
if(authenticate() == false) return null;
if(entity == null || entity.getMediaType() == null || !MediaType.MULTIPART_FORM_DATA.isCompatible(entity.getMediaType())) {
throw new ActivitiIllegalArgumentException("The request should be of type" + MediaType.MULTIPART_FORM_DATA +".");
}
RestletFileUpload upload = new RestletFileUpload(new DiskFileItemFactory());
List<FileItem> items = upload.parseRepresentation(entity);
......@@ -98,6 +103,10 @@ public class DeploymentCollectionResource extends SecuredResource {
}
}
if(uploadItem == null) {
throw new ActivitiIllegalArgumentException("No file content was found in request body.");
}
DeploymentBuilder deploymentBuilder = ActivitiUtil.getRepositoryService().createDeployment();
String fileName = uploadItem.getName();
if (fileName.endsWith(".bpmn20.xml") || fileName.endsWith(".bpmn")) {
......@@ -112,9 +121,8 @@ public class DeploymentCollectionResource extends SecuredResource {
setStatus(Status.SUCCESS_CREATED);
DeploymentResponse response = new DeploymentResponse(deployment,
createFullResourceUrl(RestUrls.URL_DEPLOYMENT, deployment.getId()));
return response;
return getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory()
.createDeploymentResponse(this, deployment);
} catch (Exception e) {
if(e instanceof ActivitiException) {
......
......@@ -18,8 +18,8 @@ import org.activiti.engine.ActivitiObjectNotFoundException;
import org.activiti.engine.repository.Deployment;
import org.activiti.rest.api.ActivitiUtil;
import org.activiti.rest.api.RequestUtil;
import org.activiti.rest.api.RestUrls;
import org.activiti.rest.api.SecuredResource;
import org.activiti.rest.application.ActivitiRestServicesApplication;
import org.restlet.data.Status;
import org.restlet.resource.Delete;
import org.restlet.resource.Get;
......@@ -45,9 +45,8 @@ public class DeploymentResource extends SecuredResource {
if(deployment == null) {
throw new ActivitiObjectNotFoundException("Could not find a deployment with id '" + deploymentId + "'.", Deployment.class);
}
DeploymentResponse response = new DeploymentResponse(deployment, createFullResourceUrl(RestUrls.URL_DEPLOYMENT, deploymentId));
return response;
return getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory()
.createDeploymentResponse(this, deployment);
}
@Delete
......
......@@ -20,8 +20,9 @@ import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.ActivitiObjectNotFoundException;
import org.activiti.engine.repository.Deployment;
import org.activiti.rest.api.ActivitiUtil;
import org.activiti.rest.api.RestUrls;
import org.activiti.rest.api.RestResponseFactory;
import org.activiti.rest.api.SecuredResource;
import org.activiti.rest.application.ActivitiRestServicesApplication;
import org.restlet.resource.Get;
......@@ -50,12 +51,9 @@ public class DeploymentResourceCollectionResource extends SecuredResource {
// Add additional metadata to the artifact-strings before returning
List<DeploymentResourceResponse> resposeList = new ArrayList<DeploymentResourceResponse>();
String resourceUrl = null;
String resourceContentUrl = null;
for(String resource : resourceList) {
resourceUrl = createFullResourceUrl(RestUrls.URL_DEPLOYMENT_RESOURCE, deploymentId, resource);
resourceContentUrl = createFullResourceUrl(RestUrls.URL_DEPLOYMENT_RESOURCE_CONTENT, deploymentId, resource);
resposeList.add(new DeploymentResourceResponse(resource, resourceUrl, resourceContentUrl));
RestResponseFactory responseFactory = getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory();
for(String resourceId : resourceList) {
resposeList.add(responseFactory.createDeploymentResourceResponse(this, deploymentId, resourceId));
}
return resposeList;
}
......
......@@ -19,8 +19,8 @@ import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.ActivitiObjectNotFoundException;
import org.activiti.engine.repository.Deployment;
import org.activiti.rest.api.ActivitiUtil;
import org.activiti.rest.api.RestUrls;
import org.activiti.rest.api.SecuredResource;
import org.activiti.rest.application.ActivitiRestServicesApplication;
import org.restlet.resource.Get;
......@@ -52,9 +52,8 @@ public class DeploymentResourceResource extends SecuredResource {
if(resourceList.contains(resourceId)) {
// Build resource representation
String resourceUrl = createFullResourceUrl(RestUrls.URL_DEPLOYMENT_RESOURCE, deploymentId, resourceId);
String resourceContentUrl = createFullResourceUrl(RestUrls.URL_DEPLOYMENT_RESOURCE_CONTENT, deploymentId, resourceId);
return new DeploymentResourceResponse(resourceId, resourceUrl, resourceContentUrl);
return getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory()
.createDeploymentResourceResponse(this, deploymentId, resourceId);
} else {
// Resource not found in deployment
throw new ActivitiObjectNotFoundException("Could not find a resource with id '" + resourceId
......
......@@ -21,11 +21,20 @@ public class DeploymentResourceResponse {
private String id;
private String url;
private String contentUrl;
private String mediaType;
private DeploymentResourceType type;
public DeploymentResourceResponse(String resourceId, String url, String contentUrl) {
public DeploymentResourceResponse(String resourceId, String url, String contentUrl,
String mediaType, DeploymentResourceType type) {
setId(resourceId);
setUrl(url);
setContentUrl(contentUrl);
setMediaType(mediaType);
this.type = type;
if(type == null) {
this.type = DeploymentResourceType.RESOURCE;
}
}
public String getId() {
......@@ -46,4 +55,41 @@ public class DeploymentResourceResponse {
public String getContentUrl() {
return contentUrl;
}
public void setMediaType(String mimeType) {
this.mediaType = mimeType;
}
public String getMediaType() {
return mediaType;
}
public String getType() {
switch (type) {
case PROCESS_DEFINITION:
return "processDefinition";
case PROCESS_IMAGE:
return "processImage";
default:
return "resource";
}
}
/**
* Possible types of resources in a deployment.
* TODO: move to activiti API
*
* @author Frederik Heremans
*/
public enum DeploymentResourceType {
/**
* A simple resource in a deployment.
*/
RESOURCE,
/**
* A resource that contains a process-definition.
*/
PROCESS_DEFINITION,
/**
* A resource that is a process-definition image.
*/
PROCESS_IMAGE;
}
}
......@@ -18,8 +18,9 @@ import java.util.List;
import org.activiti.engine.repository.Deployment;
import org.activiti.rest.api.AbstractPaginateList;
import org.activiti.rest.api.RestUrls;
import org.activiti.rest.api.RestResponseFactory;
import org.activiti.rest.api.SecuredResource;
import org.activiti.rest.application.ActivitiRestServicesApplication;
/**
* @author Tijs Rademakers
......@@ -36,10 +37,9 @@ public class DeploymentsPaginateList extends AbstractPaginateList {
@Override
protected List processList(List list) {
List<DeploymentResponse> responseList = new ArrayList<DeploymentResponse>();
RestResponseFactory restResponseFactory = resource.getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory();
for (Object deployment : list) {
responseList.add(new DeploymentResponse((Deployment) deployment,
resource.createFullResourceUrl(RestUrls.URL_DEPLOYMENT, ((Deployment)deployment).getId())));
responseList.add(restResponseFactory.createDeploymentResponse(resource, (Deployment) deployment));
}
return responseList;
}
......
......@@ -14,6 +14,8 @@
package org.activiti.rest.application;
import org.activiti.rest.api.DefaultResource;
import org.activiti.rest.api.DefaultRestResponseFactory;
import org.activiti.rest.api.RestResponseFactory;
import org.activiti.rest.filter.JsonpFilter;
import org.restlet.Restlet;
import org.restlet.routing.Router;
......@@ -21,10 +23,13 @@ import org.restlet.routing.Router;
* @author Tijs Rademakers
*/
public class ActivitiRestServicesApplication extends ActivitiRestApplication {
protected RestResponseFactory restResponseFactory;
public ActivitiRestServicesApplication() {
super();
}
/**
* Creates a root Restlet that will receive all incoming calls.
*/
......@@ -43,4 +48,16 @@ public class ActivitiRestServicesApplication extends ActivitiRestApplication {
return authenticator;
}
public void setRestResponseFactory(RestResponseFactory restResponseFactory) {
this.restResponseFactory = restResponseFactory;
}
public RestResponseFactory getRestResponseFactory() {
if(restResponseFactory == null) {
restResponseFactory = new DefaultRestResponseFactory();
}
return restResponseFactory;
}
}
......@@ -49,6 +49,8 @@ public class DeploymentResourceResourceTest extends BaseRestTestCase {
RestUrls.URL_DEPLOYMENT_RESOURCE, deployment.getId(), encodedResourceId)));
assertTrue(responseNode.get("contentUrl").getTextValue().endsWith(RestUrls.createRelativeResourceUrl(
RestUrls.URL_DEPLOYMENT_RESOURCE_CONTENT, deployment.getId(), encodedResourceId)));
assertEquals("text/xml", responseNode.get("mediaType").getTextValue());
assertEquals("processDefinition", responseNode.get("type").getTextValue());
} finally {
// Always cleanup any created deployments, even if the test failed
......
......@@ -57,6 +57,8 @@ public class DeploymentResourcesResourceTest extends BaseRestTestCase {
RestUrls.URL_DEPLOYMENT_RESOURCE, deployment.getId(), "test.txt")));
assertTrue(txtNode.get("contentUrl").getTextValue().endsWith(RestUrls.createRelativeResourceUrl(
RestUrls.URL_DEPLOYMENT_RESOURCE_CONTENT, deployment.getId(), "test.txt")));
assertTrue(txtNode.get("mediaType").isNull());
assertEquals("resource", txtNode.get("type").getTextValue());
} finally {
// Always cleanup any created deployments, even if the test failed
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册