diff --git a/config/alfresco/public-rest-context.xml b/config/alfresco/public-rest-context.xml
index bc4fa2a283..1b0a4aba4d 100644
--- a/config/alfresco/public-rest-context.xml
+++ b/config/alfresco/public-rest-context.xml
@@ -150,6 +150,10 @@
+
+
+
+
@@ -160,8 +164,7 @@
-
-
+
diff --git a/source/java/org/alfresco/rest/api/NetworkWebScriptGet.java b/source/java/org/alfresco/rest/api/NetworkWebScriptGet.java
index a156fb11e0..f00ef9a304 100644
--- a/source/java/org/alfresco/rest/api/NetworkWebScriptGet.java
+++ b/source/java/org/alfresco/rest/api/NetworkWebScriptGet.java
@@ -51,8 +51,8 @@ public class NetworkWebScriptGet extends ApiWebScript
{
// apply content type
res.setContentType(Format.JSON.mimetype() + ";charset=UTF-8");
-
- jsonHelper.withWriter(res.getOutputStream(), new Writer()
+
+ assistant.getJsonHelper().withWriter(res.getOutputStream(), new Writer()
{
@Override
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
@@ -81,17 +81,13 @@ public class NetworkWebScriptGet extends ApiWebScript
}
}, true, true);
}
- catch (ApiException apiException)
+ catch (ApiException | WebScriptException apiException)
{
- renderErrorResponse(resolveException(apiException), res);
- }
- catch (WebScriptException webException)
- {
- renderErrorResponse(resolveException(webException), res);
+ assistant.renderException(apiException, res);
}
catch (RuntimeException runtimeException)
{
- renderErrorResponse(resolveException(runtimeException), res);
+ assistant.renderException(runtimeException, res);
}
}
}
diff --git a/source/java/org/alfresco/rest/api/NetworksWebScriptGet.java b/source/java/org/alfresco/rest/api/NetworksWebScriptGet.java
index ab510dca7b..5d6ee615d4 100644
--- a/source/java/org/alfresco/rest/api/NetworksWebScriptGet.java
+++ b/source/java/org/alfresco/rest/api/NetworksWebScriptGet.java
@@ -79,8 +79,8 @@ public class NetworksWebScriptGet extends ApiWebScript
// apply content type
res.setContentType(Format.JSON.mimetype() + ";charset=UTF-8");
-
- jsonHelper.withWriter(res.getOutputStream(), new Writer()
+
+ assistant.getJsonHelper().withWriter(res.getOutputStream(), new Writer()
{
@Override
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
@@ -107,17 +107,13 @@ public class NetworksWebScriptGet extends ApiWebScript
}
}, true, true);
}
- catch (ApiException apiException)
+ catch (ApiException | WebScriptException apiException)
{
- renderErrorResponse(resolveException(apiException), res);
- }
- catch (WebScriptException webException)
- {
- renderErrorResponse(resolveException(webException), res);
+ assistant.renderException(apiException, res);
}
catch (RuntimeException runtimeException)
{
- renderErrorResponse(resolveException(runtimeException), res);
+ assistant.renderException(runtimeException, res);
}
}
}
\ No newline at end of file
diff --git a/source/java/org/alfresco/rest/api/PublicApiDeclarativeRegistry.java b/source/java/org/alfresco/rest/api/PublicApiDeclarativeRegistry.java
index 3c9616859a..f6d4d1ca64 100644
--- a/source/java/org/alfresco/rest/api/PublicApiDeclarativeRegistry.java
+++ b/source/java/org/alfresco/rest/api/PublicApiDeclarativeRegistry.java
@@ -34,6 +34,7 @@ import org.alfresco.rest.framework.resource.actions.interfaces.BinaryResourceAct
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
import org.alfresco.rest.framework.resource.actions.interfaces.ResourceAction;
+import org.alfresco.rest.framework.tools.ApiAssistant;
import org.apache.commons.lang.StringUtils;
import org.springframework.extensions.webscripts.ArgumentTypeDescription;
import org.springframework.extensions.webscripts.Container;
@@ -209,7 +210,7 @@ public class PublicApiDeclarativeRegistry extends DeclarativeRegistry
if (templateVars.get("apiName") != null)
{
// NOTE: noAuth currently only exposed for GET or Create Ticket (login)
- Api api = determineApi(templateVars);
+ Api api = ApiAssistant.determineApi(templateVars);
// TODO can we avoid locating resource more than once (or at least provide a common code to determine the GET resourceAction) ?
return locator.locateResource(api, templateVars, method);
@@ -421,15 +422,6 @@ public class PublicApiDeclarativeRegistry extends DeclarativeRegistry
return new Match(match.getTemplate(), match.getTemplateVars(), match.getPath(), noAuthWebScriptWrapper);
}
- // note: same as ApiWebscript (apiName must not be null)
- private Api determineApi(Map templateVars)
- {
- String apiScope = templateVars.get("apiScope");
- String apiVersion = templateVars.get("apiVersion");
- String apiName = templateVars.get("apiName");
- return Api.valueOf(apiName,apiScope,apiVersion);
- }
-
private void initWebScript(WebScript webScript, String name)
{
DescriptionImpl serviceDesc = new DescriptionImpl(name, name, name, name);
diff --git a/source/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRuntime.java b/source/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRuntime.java
index b1a3acc50d..53e36d5863 100644
--- a/source/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRuntime.java
+++ b/source/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRuntime.java
@@ -18,27 +18,29 @@
*/
package org.alfresco.rest.api;
+import java.io.IOException;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.repo.web.scripts.TenantWebScriptServletRuntime;
+import org.alfresco.rest.framework.tools.ApiAssistant;
import org.springframework.extensions.config.ServerProperties;
import org.springframework.extensions.surf.util.URLDecoder;
-import org.springframework.extensions.webscripts.Match;
-import org.springframework.extensions.webscripts.RuntimeContainer;
-import org.springframework.extensions.webscripts.WebScriptRequest;
+import org.springframework.extensions.webscripts.*;
import org.springframework.extensions.webscripts.servlet.ServletAuthenticatorFactory;
public class PublicApiTenantWebScriptServletRuntime extends TenantWebScriptServletRuntime
{
private static final Pattern CMIS_URI_PATTERN = Pattern.compile(".*/cmis/versions/[0-9]+\\.[0-9]+/.*");
-
+ private ApiAssistant apiAssistant;
+
public PublicApiTenantWebScriptServletRuntime(RuntimeContainer container, ServletAuthenticatorFactory authFactory, HttpServletRequest req,
- HttpServletResponse res, ServerProperties serverProperties)
+ HttpServletResponse res, ServerProperties serverProperties, ApiAssistant apiAssistant)
{
super(container, authFactory, req, res, serverProperties);
+ this.apiAssistant = apiAssistant;
}
/* (non-Javadoc)
@@ -121,4 +123,24 @@ public class PublicApiTenantWebScriptServletRuntime extends TenantWebScriptServl
{
return "PublicApiTenantServletRuntime";
}
+
+ @Override
+ protected void renderErrorResponse(Match match, Throwable exception, WebScriptRequest request, WebScriptResponse response) {
+
+ //If its cmis or not an exception then use the default behaviour
+ if (CMIS_URI_PATTERN.matcher(req.getRequestURI()).matches() || !(exception instanceof Exception))
+ {
+ super.renderErrorResponse(match, exception, request, response);
+ }
+ else
+ {
+ try {
+ apiAssistant.renderException((Exception)exception, response);
+ } catch (IOException e) {
+ logger.error("Internal error", e);
+ throw new WebScriptException("Internal error", e);
+ }
+ }
+
+ }
}
diff --git a/source/java/org/alfresco/rest/api/PublicApiWebScriptServlet.java b/source/java/org/alfresco/rest/api/PublicApiWebScriptServlet.java
index 7d08c6bbcc..a1fe9e6a39 100644
--- a/source/java/org/alfresco/rest/api/PublicApiWebScriptServlet.java
+++ b/source/java/org/alfresco/rest/api/PublicApiWebScriptServlet.java
@@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.repo.web.scripts.TenantWebScriptServlet;
+import org.alfresco.rest.framework.tools.ApiAssistant;
import org.springframework.context.ApplicationContext;
import org.springframework.extensions.webscripts.RuntimeContainer;
import org.springframework.extensions.webscripts.servlet.WebScriptServletRuntime;
@@ -33,7 +34,7 @@ import org.springframework.web.context.support.WebApplicationContextUtils;
public class PublicApiWebScriptServlet extends TenantWebScriptServlet
{
private static final long serialVersionUID = 726730674397482039L;
-
+ private ApiAssistant apiAssistant;
@Override
public void init() throws ServletException
{
@@ -41,6 +42,7 @@ public class PublicApiWebScriptServlet extends TenantWebScriptServlet
ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
container = (RuntimeContainer)context.getBean("publicapi.container");
+ apiAssistant = (ApiAssistant) context.getBean("apiAssistant");
}
/* (non-Javadoc)
@@ -55,7 +57,7 @@ public class PublicApiWebScriptServlet extends TenantWebScriptServlet
protected WebScriptServletRuntime getRuntime(HttpServletRequest req, HttpServletResponse res)
{
- WebScriptServletRuntime runtime = new PublicApiTenantWebScriptServletRuntime(container, authenticatorFactory, req, res, serverProperties);
+ WebScriptServletRuntime runtime = new PublicApiTenantWebScriptServletRuntime(container, authenticatorFactory, req, res, serverProperties, apiAssistant);
return runtime;
}
}
diff --git a/source/java/org/alfresco/rest/framework/tools/ApiAssistant.java b/source/java/org/alfresco/rest/framework/tools/ApiAssistant.java
new file mode 100644
index 0000000000..7818c75e4c
--- /dev/null
+++ b/source/java/org/alfresco/rest/framework/tools/ApiAssistant.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2005-2016 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.rest.framework.tools;
+
+import org.alfresco.rest.framework.Api;
+import org.alfresco.rest.framework.core.exceptions.DefaultExceptionResolver;
+import org.alfresco.rest.framework.core.exceptions.ErrorResponse;
+import org.alfresco.rest.framework.core.exceptions.ExceptionResolver;
+import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
+import org.alfresco.rest.framework.resource.content.ContentInfo;
+import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.json.simple.JSONObject;
+import org.springframework.extensions.surf.util.I18NUtil;
+import org.springframework.extensions.webscripts.*;
+import org.springframework.extensions.webscripts.Description.RequiredCache;
+import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Assists you in creating a great Rest API.
+ *
+ * @author Gethin James
+ */
+public class ApiAssistant {
+
+ private static Log logger = LogFactory.getLog(ApiAssistant.class);
+
+ public final static String UTF8 = "UTF-8";
+ public final static ContentInfo DEFAULT_JSON_CONTENT = new ContentInfoImpl(Format.JSON.mimetype(),UTF8, 0, null);
+ public final static Cache CACHE_NEVER = new Cache(new RequiredCache()
+ {
+ @Override
+ public boolean getNeverCache()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean getIsPublic()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean getMustRevalidate()
+ {
+ return true;
+ }
+ });
+
+ private ExceptionResolver defaultResolver = new DefaultExceptionResolver();
+ private ExceptionResolver resolver;
+ private JacksonHelper jsonHelper;
+
+ public static Api determineApi(Map templateVars)
+ {
+ String apiScope = templateVars.get("apiScope");
+ String apiVersion = templateVars.get("apiVersion");
+ String apiName = templateVars.get("apiName");
+ return Api.valueOf(apiName,apiScope,apiVersion);
+ }
+
+ public ErrorResponse resolveException(Exception ex)
+ {
+ ErrorResponse error = resolver.resolveException(ex);
+ if (error == null)
+ {
+ error = defaultResolver.resolveException(ex);
+ }
+ return error;
+ }
+
+ /**
+ * Sets the response headers with any information we know about the content
+ * @param res WebScriptResponse
+ * @param contentInfo Content Information
+ */
+ public void setContentInfoOnResponse(WebScriptResponse res, ContentInfo contentInfo)
+ {
+ if (contentInfo != null)
+ {
+ //Set content info on the response
+ res.setContentType(contentInfo.getMimeType());
+ res.setContentEncoding(contentInfo.getEncoding());
+
+ if (res instanceof WrappingWebScriptResponse)
+ {
+ WrappingWebScriptResponse wrappedRes = ((WrappingWebScriptResponse) res);
+ res = wrappedRes.getNext();
+ }
+
+ if (res instanceof WebScriptServletResponse)
+ {
+ WebScriptServletResponse servletResponse = (WebScriptServletResponse) res;
+ if (contentInfo.getLength() > 0)
+ {
+ if (contentInfo.getLength() > 0 && contentInfo.getLength() < Integer.MAX_VALUE)
+ {
+ servletResponse.getHttpServletResponse().setContentLength((int) contentInfo.getLength());
+ }
+ }
+ if (contentInfo.getLocale() != null)
+ {
+ servletResponse.getHttpServletResponse().setLocale(contentInfo.getLocale());
+ }
+ }
+ }
+ }
+
+ /**
+ * Renders an exception to the output stream as Json.
+ * @param exception
+ * @param response
+ * @throws IOException
+ */
+ public void renderException(Exception exception, final WebScriptResponse response) throws IOException {
+ renderErrorResponse(resolveException(exception), response);
+ }
+
+ /**
+ * Renders a JSON error response
+ * @param errorResponse The error
+ * @param res web script response
+ * @throws IOException
+ */
+ public void renderErrorResponse(ErrorResponse errorResponse, final WebScriptResponse res) throws IOException {
+
+ String logId = "";
+
+ if (Status.STATUS_INTERNAL_SERVER_ERROR == errorResponse.getStatusCode() || logger.isDebugEnabled())
+ {
+ logId = org.alfresco.util.GUID.generate();
+ logger.error(logId+" : "+errorResponse.getStackTrace());
+ }
+
+ String stackMessage = I18NUtil.getMessage(DefaultExceptionResolver.STACK_MESSAGE_ID);
+
+ final ErrorResponse errorToWrite = new ErrorResponse(errorResponse.getErrorKey(),
+ errorResponse.getStatusCode(),
+ errorResponse.getBriefSummary(),
+ stackMessage,
+ logId,
+ errorResponse.getAdditionalState(),
+ DefaultExceptionResolver.ERROR_URL);
+
+ setContentInfoOnResponse(res, DEFAULT_JSON_CONTENT);
+
+ // Status must be set before the response is written by Jackson (which will by default close and commit the response).
+ // In a r/w txn, web script buffered responses ensure that it doesn't really matter but for r/o txns this is important.
+ res.setStatus(errorToWrite.getStatusCode());
+
+ jsonHelper.withWriter(res.getOutputStream(), new JacksonHelper.Writer()
+ {
+ @SuppressWarnings("unchecked")
+ @Override
+ public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
+ throws JsonGenerationException, JsonMappingException, IOException
+ {
+ JSONObject obj = new JSONObject();
+ obj.put("error", errorToWrite);
+ objectMapper.writeValue(generator, obj);
+ }
+ });
+ }
+
+ public JacksonHelper getJsonHelper() {
+ return jsonHelper;
+ }
+
+ public void setDefaultResolver(ExceptionResolver defaultResolver) {
+ this.defaultResolver = defaultResolver;
+ }
+
+ public void setResolver(ExceptionResolver resolver) {
+ this.resolver = resolver;
+ }
+
+ public void setJsonHelper(JacksonHelper jsonHelper) {
+ this.jsonHelper = jsonHelper;
+ }
+}
diff --git a/source/java/org/alfresco/rest/framework/webscripts/AbstractResourceWebScript.java b/source/java/org/alfresco/rest/framework/webscripts/AbstractResourceWebScript.java
index 55aa49795d..464c59d1b6 100644
--- a/source/java/org/alfresco/rest/framework/webscripts/AbstractResourceWebScript.java
+++ b/source/java/org/alfresco/rest/framework/webscripts/AbstractResourceWebScript.java
@@ -45,6 +45,7 @@ import org.alfresco.rest.framework.resource.content.ContentInfo;
import org.alfresco.rest.framework.resource.content.FileBinaryResource;
import org.alfresco.rest.framework.resource.content.NodeBinaryResource;
import org.alfresco.rest.framework.resource.parameters.Params;
+import org.alfresco.rest.framework.tools.ApiAssistant;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -145,11 +146,11 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
}
catch (AlfrescoRuntimeException | ApiException | WebScriptException xception )
{
- renderErrorResponse(resolveException(xception), res);
+ assistant.renderException(xception, res);
}
catch (RuntimeException runtimeException)
{
- renderErrorResponse(resolveException(runtimeException), res);
+ assistant.renderException(runtimeException, res);
}
}
@@ -157,7 +158,7 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
{
final String entityCollectionName = ResourceInspector.findEntityCollectionNameName(resource.getMetaData());
final ResourceOperation operation = resource.getMetaData().getOperation(getHttpMethod());
- final WithResponse callBack = new WithResponse(operation.getSuccessStatus(),DEFAULT_JSON_CONTENT,ApiWebScript.CACHE_NEVER);
+ final WithResponse callBack = new WithResponse(operation.getSuccessStatus(), ApiAssistant.DEFAULT_JSON_CONTENT,ApiAssistant.CACHE_NEVER);
Object toReturn = transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionHelper.RetryingTransactionCallback