Merged API-STRIKES-BACK (5.2.0) to HEAD (5.2)

126242 gjames: RA-878:All api errors should return the standard error object


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@127571 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jamal Kaabi-Mofrad
2016-06-02 21:40:03 +00:00
parent 00c3315166
commit f17a4d19cf
24 changed files with 382 additions and 236 deletions

View File

@@ -150,6 +150,10 @@
<entry key="org.alfresco.rest.framework.core.exceptions.UnsupportedMediaTypeException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_UNSUPPORTED_MEDIA_TYPE}" /> <entry key="org.alfresco.rest.framework.core.exceptions.UnsupportedMediaTypeException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_UNSUPPORTED_MEDIA_TYPE}" />
</map> </map>
</property> </property>
</bean>
<bean id="apiAssistant" class="org.alfresco.rest.framework.tools.ApiAssistant">
<property name="jsonHelper" ref="jsonHelper" />
<property name="resolver" ref="simpleMappingExceptionResolver" />
</bean> </bean>
<!-- Using annotation-config=false means AutowiredAnnotationBeanPostProcessor <!-- Using annotation-config=false means AutowiredAnnotationBeanPostProcessor
and CommonAnnotationBeanPostProcessor are both NOT included implicitly --> and CommonAnnotationBeanPostProcessor are both NOT included implicitly -->
@@ -160,8 +164,7 @@
<context:component-scan base-package="org.alfresco.rest.api.modules"/> <context:component-scan base-package="org.alfresco.rest.api.modules"/>
<bean id="apiWebScriptParent" abstract="true" parent="webscript" init-method="init"> <bean id="apiWebScriptParent" abstract="true" parent="webscript" init-method="init">
<property name="resolver" ref="simpleMappingExceptionResolver" /> <property name="assistant" ref="apiAssistant" />
<property name="jsonHelper" ref="jsonHelper" />
<property name="encryptTempFiles" value="${webscripts.encryptTempFiles}"/> <property name="encryptTempFiles" value="${webscripts.encryptTempFiles}"/>
<property name="tempDirectoryName" value="${webscripts.tempDirectoryName}"/> <property name="tempDirectoryName" value="${webscripts.tempDirectoryName}"/>
<property name="memoryThreshold" value="${webscripts.memoryThreshold}"/> <property name="memoryThreshold" value="${webscripts.memoryThreshold}"/>

View File

@@ -52,7 +52,7 @@ public class NetworkWebScriptGet extends ApiWebScript
// apply content type // apply content type
res.setContentType(Format.JSON.mimetype() + ";charset=UTF-8"); res.setContentType(Format.JSON.mimetype() + ";charset=UTF-8");
jsonHelper.withWriter(res.getOutputStream(), new Writer() assistant.getJsonHelper().withWriter(res.getOutputStream(), new Writer()
{ {
@Override @Override
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper) public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
@@ -81,17 +81,13 @@ public class NetworkWebScriptGet extends ApiWebScript
} }
}, true, true); }, true, true);
} }
catch (ApiException apiException) catch (ApiException | WebScriptException apiException)
{ {
renderErrorResponse(resolveException(apiException), res); assistant.renderException(apiException, res);
}
catch (WebScriptException webException)
{
renderErrorResponse(resolveException(webException), res);
} }
catch (RuntimeException runtimeException) catch (RuntimeException runtimeException)
{ {
renderErrorResponse(resolveException(runtimeException), res); assistant.renderException(runtimeException, res);
} }
} }
} }

View File

@@ -80,7 +80,7 @@ public class NetworksWebScriptGet extends ApiWebScript
// apply content type // apply content type
res.setContentType(Format.JSON.mimetype() + ";charset=UTF-8"); res.setContentType(Format.JSON.mimetype() + ";charset=UTF-8");
jsonHelper.withWriter(res.getOutputStream(), new Writer() assistant.getJsonHelper().withWriter(res.getOutputStream(), new Writer()
{ {
@Override @Override
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper) public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
@@ -107,17 +107,13 @@ public class NetworksWebScriptGet extends ApiWebScript
} }
}, true, true); }, true, true);
} }
catch (ApiException apiException) catch (ApiException | WebScriptException apiException)
{ {
renderErrorResponse(resolveException(apiException), res); assistant.renderException(apiException, res);
}
catch (WebScriptException webException)
{
renderErrorResponse(resolveException(webException), res);
} }
catch (RuntimeException runtimeException) catch (RuntimeException runtimeException)
{ {
renderErrorResponse(resolveException(runtimeException), res); assistant.renderException(runtimeException, res);
} }
} }
} }

View File

@@ -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.EntityResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
import org.alfresco.rest.framework.resource.actions.interfaces.ResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.ResourceAction;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.springframework.extensions.webscripts.ArgumentTypeDescription; import org.springframework.extensions.webscripts.ArgumentTypeDescription;
import org.springframework.extensions.webscripts.Container; import org.springframework.extensions.webscripts.Container;
@@ -209,7 +210,7 @@ public class PublicApiDeclarativeRegistry extends DeclarativeRegistry
if (templateVars.get("apiName") != null) if (templateVars.get("apiName") != null)
{ {
// NOTE: noAuth currently only exposed for GET or Create Ticket (login) // 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) ? // 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); 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); return new Match(match.getTemplate(), match.getTemplateVars(), match.getPath(), noAuthWebScriptWrapper);
} }
// note: same as ApiWebscript (apiName must not be null)
private Api determineApi(Map<String, String> 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) private void initWebScript(WebScript webScript, String name)
{ {
DescriptionImpl serviceDesc = new DescriptionImpl(name, name, name, name); DescriptionImpl serviceDesc = new DescriptionImpl(name, name, name, name);

View File

@@ -18,27 +18,29 @@
*/ */
package org.alfresco.rest.api; package org.alfresco.rest.api;
import java.io.IOException;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.alfresco.repo.web.scripts.TenantWebScriptServletRuntime; import org.alfresco.repo.web.scripts.TenantWebScriptServletRuntime;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.springframework.extensions.config.ServerProperties; import org.springframework.extensions.config.ServerProperties;
import org.springframework.extensions.surf.util.URLDecoder; import org.springframework.extensions.surf.util.URLDecoder;
import org.springframework.extensions.webscripts.Match; import org.springframework.extensions.webscripts.*;
import org.springframework.extensions.webscripts.RuntimeContainer;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.servlet.ServletAuthenticatorFactory; import org.springframework.extensions.webscripts.servlet.ServletAuthenticatorFactory;
public class PublicApiTenantWebScriptServletRuntime extends TenantWebScriptServletRuntime public class PublicApiTenantWebScriptServletRuntime extends TenantWebScriptServletRuntime
{ {
private static final Pattern CMIS_URI_PATTERN = Pattern.compile(".*/cmis/versions/[0-9]+\\.[0-9]+/.*"); 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, public PublicApiTenantWebScriptServletRuntime(RuntimeContainer container, ServletAuthenticatorFactory authFactory, HttpServletRequest req,
HttpServletResponse res, ServerProperties serverProperties) HttpServletResponse res, ServerProperties serverProperties, ApiAssistant apiAssistant)
{ {
super(container, authFactory, req, res, serverProperties); super(container, authFactory, req, res, serverProperties);
this.apiAssistant = apiAssistant;
} }
/* (non-Javadoc) /* (non-Javadoc)
@@ -121,4 +123,24 @@ public class PublicApiTenantWebScriptServletRuntime extends TenantWebScriptServl
{ {
return "PublicApiTenantServletRuntime"; 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);
}
}
}
} }

View File

@@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.alfresco.repo.web.scripts.TenantWebScriptServlet; import org.alfresco.repo.web.scripts.TenantWebScriptServlet;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.extensions.webscripts.RuntimeContainer; import org.springframework.extensions.webscripts.RuntimeContainer;
import org.springframework.extensions.webscripts.servlet.WebScriptServletRuntime; import org.springframework.extensions.webscripts.servlet.WebScriptServletRuntime;
@@ -33,7 +34,7 @@ import org.springframework.web.context.support.WebApplicationContextUtils;
public class PublicApiWebScriptServlet extends TenantWebScriptServlet public class PublicApiWebScriptServlet extends TenantWebScriptServlet
{ {
private static final long serialVersionUID = 726730674397482039L; private static final long serialVersionUID = 726730674397482039L;
private ApiAssistant apiAssistant;
@Override @Override
public void init() throws ServletException public void init() throws ServletException
{ {
@@ -41,6 +42,7 @@ public class PublicApiWebScriptServlet extends TenantWebScriptServlet
ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
container = (RuntimeContainer)context.getBean("publicapi.container"); container = (RuntimeContainer)context.getBean("publicapi.container");
apiAssistant = (ApiAssistant) context.getBean("apiAssistant");
} }
/* (non-Javadoc) /* (non-Javadoc)
@@ -55,7 +57,7 @@ public class PublicApiWebScriptServlet extends TenantWebScriptServlet
protected WebScriptServletRuntime getRuntime(HttpServletRequest req, HttpServletResponse res) 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; return runtime;
} }
} }

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<Exception> defaultResolver = new DefaultExceptionResolver();
private ExceptionResolver<Exception> resolver;
private JacksonHelper jsonHelper;
public static Api determineApi(Map<String, String> 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<Exception> defaultResolver) {
this.defaultResolver = defaultResolver;
}
public void setResolver(ExceptionResolver<Exception> resolver) {
this.resolver = resolver;
}
public void setJsonHelper(JacksonHelper jsonHelper) {
this.jsonHelper = jsonHelper;
}
}

View File

@@ -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.FileBinaryResource;
import org.alfresco.rest.framework.resource.content.NodeBinaryResource; import org.alfresco.rest.framework.resource.content.NodeBinaryResource;
import org.alfresco.rest.framework.resource.parameters.Params; 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.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@@ -145,11 +146,11 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
} }
catch (AlfrescoRuntimeException | ApiException | WebScriptException xception ) catch (AlfrescoRuntimeException | ApiException | WebScriptException xception )
{ {
renderErrorResponse(resolveException(xception), res); assistant.renderException(xception, res);
} }
catch (RuntimeException runtimeException) 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 String entityCollectionName = ResourceInspector.findEntityCollectionNameName(resource.getMetaData());
final ResourceOperation operation = resource.getMetaData().getOperation(getHttpMethod()); 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( Object toReturn = transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionHelper.RetryingTransactionCallback<Object>() new RetryingTransactionHelper.RetryingTransactionCallback<Object>()
{ {
@@ -191,7 +192,7 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
{ {
NodeBinaryResource nodeResource = (NodeBinaryResource) resource; NodeBinaryResource nodeResource = (NodeBinaryResource) resource;
ContentInfo contentInfo = nodeResource.getContentInfo(); ContentInfo contentInfo = nodeResource.getContentInfo();
setContentInfoOnResponse(res, contentInfo); assistant.setContentInfoOnResponse(res, contentInfo);
// if requested, set attachment // if requested, set attachment
boolean attach = StringUtils.isNotEmpty(nodeResource.getAttachFileName()); boolean attach = StringUtils.isNotEmpty(nodeResource.getAttachFileName());
Map<String, Object> model = getModelForCacheDirective(nodeResource.getCacheDirective()); Map<String, Object> model = getModelForCacheDirective(nodeResource.getCacheDirective());
@@ -226,7 +227,7 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
{ {
res.setStatus(status); res.setStatus(status);
if (cache != null) res.setCache(cache); if (cache != null) res.setCache(cache);
setContentInfoOnResponse(res,contentInfo); assistant.setContentInfoOnResponse(res,contentInfo);
if (headers != null && !headers.isEmpty()) if (headers != null && !headers.isEmpty())
{ {
for (Map.Entry<String, List<String>> header:headers.entrySet()) for (Map.Entry<String, List<String>> header:headers.entrySet())
@@ -262,7 +263,7 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
protected void renderJsonResponse(final WebScriptResponse res, final Object toSerialize) protected void renderJsonResponse(final WebScriptResponse res, final Object toSerialize)
throws IOException throws IOException
{ {
jsonHelper.withWriter(res.getOutputStream(), new JacksonHelper.Writer() assistant.getJsonHelper().withWriter(res.getOutputStream(), new JacksonHelper.Writer()
{ {
@Override @Override
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper) public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)

View File

@@ -32,6 +32,7 @@ import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
import org.alfresco.rest.framework.jacksonextensions.JacksonHelper.Writer; import org.alfresco.rest.framework.jacksonextensions.JacksonHelper.Writer;
import org.alfresco.rest.framework.resource.content.ContentInfo; import org.alfresco.rest.framework.resource.content.ContentInfo;
import org.alfresco.rest.framework.resource.content.ContentInfoImpl; import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.GUID; import org.alfresco.util.GUID;
import org.alfresco.util.TempFileProvider; import org.alfresco.util.TempFileProvider;
@@ -57,10 +58,7 @@ import org.springframework.extensions.webscripts.servlet.WebScriptServletRespons
public abstract class ApiWebScript extends AbstractWebScript public abstract class ApiWebScript extends AbstractWebScript
{ {
private static Log logger = LogFactory.getLog(ApiWebScript.class); private static Log logger = LogFactory.getLog(ApiWebScript.class);
protected JacksonHelper jsonHelper; protected ApiAssistant assistant;
ExceptionResolver<Exception> defaultResolver = new DefaultExceptionResolver();
ExceptionResolver<Exception> resolver;
protected boolean encryptTempFiles = false; protected boolean encryptTempFiles = false;
protected String tempDirectoryName = null; protected String tempDirectoryName = null;
protected int memoryThreshold = 4 * 1024 * 1024; // 4mb protected int memoryThreshold = 4 * 1024 * 1024; // 4mb
@@ -73,9 +71,8 @@ public abstract class ApiWebScript extends AbstractWebScript
this.transactionService = transactionService; this.transactionService = transactionService;
} }
public void setDefaultResolver(ExceptionResolver<Exception> defaultResolver) public void setAssistant(ApiAssistant assistant) {
{ this.assistant = assistant;
this.defaultResolver = defaultResolver;
} }
public void setTempDirectoryName(String tempDirectoryName) public void setTempDirectoryName(String tempDirectoryName)
@@ -109,34 +106,11 @@ public abstract class ApiWebScript extends AbstractWebScript
this.streamFactory = ThresholdOutputStreamFactory.newInstance(tempDirectory, memoryThreshold, maxContentSize, encryptTempFiles); this.streamFactory = ThresholdOutputStreamFactory.newInstance(tempDirectory, memoryThreshold, maxContentSize, encryptTempFiles);
} }
public final static String UTF8 = "UTF-8";
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;
}
});
public final static ContentInfo DEFAULT_JSON_CONTENT = new ContentInfoImpl(Format.JSON.mimetype(),UTF8, 0, null);
@Override @Override
public void execute(final WebScriptRequest req, final WebScriptResponse res) throws IOException public void execute(final WebScriptRequest req, final WebScriptResponse res) throws IOException
{ {
Map<String, String> templateVars = req.getServiceMatch().getTemplateVars(); Map<String, String> templateVars = req.getServiceMatch().getTemplateVars();
Api api = determineApi(templateVars); Api api = assistant.determineApi(templateVars);
final BufferedRequest bufferedReq = getRequest(req); final BufferedRequest bufferedReq = getRequest(req);
final BufferedResponse bufferedRes = getResponse(res); final BufferedResponse bufferedRes = getResponse(res);
@@ -161,24 +135,6 @@ public abstract class ApiWebScript extends AbstractWebScript
} }
} }
public Api determineApi(Map<String, String> templateVars)
{
String apiScope = templateVars.get("apiScope");
String apiVersion = templateVars.get("apiVersion");
String apiName = templateVars.get("apiName");
return Api.valueOf(apiName,apiScope,apiVersion);
}
protected ErrorResponse resolveException(Exception ex)
{
ErrorResponse error = resolver.resolveException(ex);
if (error == null)
{
error = defaultResolver.resolveException(ex);
}
return error;
}
protected BufferedRequest getRequest(final WebScriptRequest req) protected BufferedRequest getRequest(final WebScriptRequest req)
{ {
// create buffered request and response that allow transaction retrying // create buffered request and response that allow transaction retrying
@@ -195,97 +151,4 @@ public abstract class ApiWebScript extends AbstractWebScript
public abstract void execute(final Api api, WebScriptRequest req, WebScriptResponse res) throws IOException; public abstract void execute(final Api api, WebScriptRequest req, WebScriptResponse res) throws IOException;
/**
* 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 = 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 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);
}
});
}
/**
* Sets the response headers with any information we know about the content
* @param res WebScriptResponse
* @param contentInfo Content Information
*/
protected 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());
}
}
}
}
public void setResolver(ExceptionResolver<Exception> resolver)
{
this.resolver = resolver;
}
public void setJsonHelper(JacksonHelper jsonHelper)
{
this.jsonHelper = jsonHelper;
}
} }

View File

@@ -12,6 +12,7 @@ import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAct
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
import org.alfresco.rest.framework.resource.parameters.Params; 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.lang.StringUtils;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse; import org.springframework.extensions.webscripts.WebScriptResponse;
@@ -189,7 +190,7 @@ public class ResourceWebScriptDelete extends AbstractResourceWebScript implement
public Void execute(final ResourceWithMetadata resource, final Params params, final WebScriptResponse res, boolean isReadOnly) public Void execute(final ResourceWithMetadata resource, final Params params, final WebScriptResponse res, boolean isReadOnly)
{ {
final ResourceOperation operation = resource.getMetaData().getOperation(HttpMethod.DELETE); final ResourceOperation operation = resource.getMetaData().getOperation(HttpMethod.DELETE);
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);
transactionService.getRetryingTransactionHelper().doInTransaction( transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionCallback<Void>() new RetryingTransactionCallback<Void>()
{ {

View File

@@ -58,6 +58,7 @@ import org.alfresco.rest.framework.resource.parameters.where.InvalidQueryExcepti
import org.alfresco.rest.framework.resource.parameters.where.Query; import org.alfresco.rest.framework.resource.parameters.where.Query;
import org.alfresco.rest.framework.resource.parameters.where.QueryImpl; import org.alfresco.rest.framework.resource.parameters.where.QueryImpl;
import org.alfresco.rest.framework.resource.parameters.where.WhereCompiler; import org.alfresco.rest.framework.resource.parameters.where.WhereCompiler;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck; import org.alfresco.util.PropertyCheck;
import org.antlr.runtime.RecognitionException; import org.antlr.runtime.RecognitionException;
@@ -668,7 +669,7 @@ public class ResourceWebScriptHelper
paramFilter = filters.get(resourceKey); paramFilter = filters.get(resourceKey);
} }
final Params executionParams = Params.valueOf(paramFilter, uniqueEntityId, params.getRequest()); final Params executionParams = Params.valueOf(paramFilter, uniqueEntityId, params.getRequest());
final WithResponse callBack = new WithResponse(Status.STATUS_OK,ApiWebScript.DEFAULT_JSON_CONTENT,ApiWebScript.CACHE_NEVER); final WithResponse callBack = new WithResponse(Status.STATUS_OK, ApiAssistant.DEFAULT_JSON_CONTENT,ApiAssistant.CACHE_NEVER);
//Read only because this only occurs for GET requests //Read only because this only occurs for GET requests
Object result = executor.executeAction(resource, executionParams, callBack); Object result = executor.executeAction(resource, executionParams, callBack);
return processAdditionsToTheResponse(null, api, null, executionParams, result); return processAdditionsToTheResponse(null, api, null, executionParams, result);

View File

@@ -102,7 +102,7 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
if (objectType!= null) if (objectType!= null)
{ {
//Operations don't support a List as json body //Operations don't support a List as json body
postedObj = ResourceWebScriptHelper.extractJsonContent(req, jsonHelper, objectType); postedObj = ResourceWebScriptHelper.extractJsonContent(req, assistant.getJsonHelper(), objectType);
} }
if (StringUtils.isNotBlank(propertyName)) if (StringUtils.isNotBlank(propertyName))
@@ -156,7 +156,7 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
// Only allow 1 value. // Only allow 1 value.
try try
{ {
Object content = ResourceWebScriptHelper.extractJsonContent(req,jsonHelper, objType); Object content = ResourceWebScriptHelper.extractJsonContent(req,assistant.getJsonHelper(), objType);
return Arrays.asList(content); return Arrays.asList(content);
} }
catch (InvalidArgumentException iae) catch (InvalidArgumentException iae)
@@ -173,7 +173,7 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
} }
} }
} }
return ResourceWebScriptHelper.extractJsonContentAsList(req, jsonHelper, objType); return ResourceWebScriptHelper.extractJsonContentAsList(req, assistant.getJsonHelper(), objType);
} }

View File

@@ -81,7 +81,7 @@ public class ResourceWebScriptPut extends AbstractResourceWebScript implements P
} else } else
{ {
Object putEnt = ResourceWebScriptHelper.extractJsonContent(req, jsonHelper, resourceMeta.getObjectType(operation)); Object putEnt = ResourceWebScriptHelper.extractJsonContent(req, assistant.getJsonHelper(), resourceMeta.getObjectType(operation));
return Params.valueOf(entityId,params,putEnt, req); return Params.valueOf(entityId,params,putEnt, req);
} }
case RELATIONSHIP: case RELATIONSHIP:
@@ -90,7 +90,7 @@ public class ResourceWebScriptPut extends AbstractResourceWebScript implements P
throw new UnsupportedResourceOperationException("PUT is executed against the instance URL"); throw new UnsupportedResourceOperationException("PUT is executed against the instance URL");
} else } else
{ {
Object putRel = ResourceWebScriptHelper.extractJsonContent(req, jsonHelper, resourceMeta.getObjectType(operation)); Object putRel = ResourceWebScriptHelper.extractJsonContent(req, assistant.getJsonHelper(), resourceMeta.getObjectType(operation));
ResourceWebScriptHelper.setUniqueId(putRel,relationshipId); ResourceWebScriptHelper.setUniqueId(putRel,relationshipId);
return Params.valueOf(entityId, params, putRel, req); return Params.valueOf(entityId, params, putRel, req);
} }

View File

@@ -53,7 +53,7 @@ public class InfoWebScriptGet extends ApiWebScript
throw new InvalidArgumentException(InvalidArgumentException.DEFAULT_INVALID_API); throw new InvalidArgumentException(InvalidArgumentException.DEFAULT_INVALID_API);
} }
jsonHelper.withWriter(res.getOutputStream(), new Writer() assistant.getJsonHelper().withWriter(res.getOutputStream(), new Writer()
{ {
@Override @Override
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper) public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)

View File

@@ -131,7 +131,7 @@ public class WebScriptOptionsMetaData extends ApiWebScript implements ResourceMe
final Object result = processResult(resource,allApiResources); final Object result = processResult(resource,allApiResources);
jsonHelper.withWriter(out, new Writer() assistant.getJsonHelper().withWriter(out, new Writer()
{ {
@Override @Override
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper) public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)

View File

@@ -1,3 +1,21 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.rest; package org.alfresco.rest;
import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsStringNonNull; import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsStringNonNull;
@@ -21,7 +39,7 @@ import org.junit.After;
import org.junit.Before; import org.junit.Before;
/** /**
* Created by gethin on 31/03/16. * @author Gethin James
*/ */
public class AbstractSingleNetworkSiteTest extends AbstractBaseApiTest public class AbstractSingleNetworkSiteTest extends AbstractBaseApiTest
{ {

View File

@@ -29,11 +29,14 @@ import static org.junit.Assert.assertTrue;
import org.alfresco.rest.api.model.ModulePackage; import org.alfresco.rest.api.model.ModulePackage;
import org.alfresco.rest.api.tests.client.HttpResponse; import org.alfresco.rest.api.tests.client.HttpResponse;
import org.alfresco.rest.api.tests.client.PublicApiClient; import org.alfresco.rest.api.tests.client.PublicApiClient;
import org.alfresco.rest.api.tests.client.RequestContext;
import org.alfresco.rest.api.tests.util.RestApiUtil;
import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.HttpStatus;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Basic modulepackages api calls * Basic modulepackages api calls
@@ -82,6 +85,26 @@ public class ModulePackagesApiTest extends AbstractBaseApiTest
assertTrue("Simple module must be the correct version","1.0.0-SNAPSHOT".equals(simpleModule.getVersion().toString())); assertTrue("Simple module must be the correct version","1.0.0-SNAPSHOT".equals(simpleModule.getVersion().toString()));
} }
@Test
public void testErrorUrls() throws Exception
{
publicApiClient.setRequestContext(new RequestContext(null));
Map<String, String> params = createParams(null, null);
//Call an endpoint that doesn't exist
HttpResponse response = publicApiClient.get(getScope(), MODULEPACKAGES+"/fred/blogs/king/kong/got/if/wrong", null, null, null, params);
assertNotNull(response);
assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
assertEquals("no-cache", response.getHeaders().get("Cache-Control"));
assertEquals("application/json;charset=UTF-8", response.getHeaders().get("Content-Type"));
PublicApiClient.ExpectedErrorResponse errorResponse = RestApiUtil.parseErrorResponse(response.getJsonResponse());
assertNotNull(errorResponse);
assertNotNull(errorResponse.getErrorKey());
assertNotNull(errorResponse.getBriefSummary());
}
@Override @Override
public String getScope() public String getScope()
{ {

View File

@@ -31,6 +31,7 @@ import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
import org.alfresco.rest.framework.resource.EntityResource; import org.alfresco.rest.framework.resource.EntityResource;
import org.alfresco.rest.framework.resource.RelationshipResource; import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.parameters.Params; import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.rest.framework.webscripts.AbstractResourceWebScript; import org.alfresco.rest.framework.webscripts.AbstractResourceWebScript;
import org.alfresco.rest.framework.webscripts.ApiWebScript; import org.alfresco.rest.framework.webscripts.ApiWebScript;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper; import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper;
@@ -68,9 +69,12 @@ public abstract class AbstractContextTest
@Autowired @Autowired
JacksonHelper jsonHelper; JacksonHelper jsonHelper;
@Autowired
ApiAssistant apiAssistant;
static Params NOT_USED = Params.valueOf("notUsed", null, mock(WebScriptRequest.class)); static Params NOT_USED = Params.valueOf("notUsed", null, mock(WebScriptRequest.class));
static final Params.RecognizedParams NULL_PARAMS = new Params.RecognizedParams(null, null, null, null, null, null, null, null, false); static final Params.RecognizedParams NULL_PARAMS = new Params.RecognizedParams(null, null, null, null, null, null, null, null, false);
static final WithResponse callBack = new WithResponse(Status.STATUS_OK, ApiWebScript.DEFAULT_JSON_CONTENT,ApiWebScript.CACHE_NEVER); static final WithResponse callBack = new WithResponse(Status.STATUS_OK, ApiAssistant.DEFAULT_JSON_CONTENT,ApiAssistant.CACHE_NEVER);
static Api api = Api.valueOf("alfrescomock", "private", "1"); static Api api = Api.valueOf("alfrescomock", "private", "1");
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -103,4 +107,18 @@ public abstract class AbstractContextTest
putExecutor.setTransactionService(transerv); putExecutor.setTransactionService(transerv);
deleteExecutor.setTransactionService(transerv); deleteExecutor.setTransactionService(transerv);
} }
protected AbstractResourceWebScript getExecutor()
{
return getExecutor("executorOfGets");
}
protected AbstractResourceWebScript getExecutor(String beanName)
{
AbstractResourceWebScript executor = (AbstractResourceWebScript) applicationContext.getBean(beanName);
executor.setLocator(locator);
executor.setAssistant(apiAssistant);
return executor;
}
} }

View File

@@ -24,6 +24,7 @@ import org.alfresco.rest.framework.tests.api.mocks.Goat;
import org.alfresco.rest.framework.tests.api.mocks.Grass; import org.alfresco.rest.framework.tests.api.mocks.Grass;
import org.alfresco.rest.framework.tests.api.mocks.Sheep; import org.alfresco.rest.framework.tests.api.mocks.Sheep;
import org.alfresco.rest.framework.tests.api.mocks3.FlockEntityResource; import org.alfresco.rest.framework.tests.api.mocks3.FlockEntityResource;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.rest.framework.webscripts.AbstractResourceWebScript; import org.alfresco.rest.framework.webscripts.AbstractResourceWebScript;
import org.alfresco.rest.framework.webscripts.ApiWebScript; import org.alfresco.rest.framework.webscripts.ApiWebScript;
import org.junit.Test; import org.junit.Test;
@@ -64,7 +65,7 @@ public class ExecutionTests extends AbstractContextTest
public void testInvokeGet() throws IOException public void testInvokeGet() throws IOException
{ {
ResourceWithMetadata entityResource = locator.locateEntityResource(api,"sheep", HttpMethod.GET); ResourceWithMetadata entityResource = locator.locateEntityResource(api,"sheep", HttpMethod.GET);
AbstractResourceWebScript executor = (AbstractResourceWebScript) applicationContext.getBean("executorOfGets"); AbstractResourceWebScript executor = getExecutor();
Object result = executor.execute(entityResource, Params.valueOf((String)null, null, mock(WebScriptRequest.class)), mock(WebScriptResponse.class), true); Object result = executor.execute(entityResource, Params.valueOf((String)null, null, mock(WebScriptRequest.class)), mock(WebScriptResponse.class), true);
assertNotNull(result); assertNotNull(result);
@@ -72,7 +73,7 @@ public class ExecutionTests extends AbstractContextTest
entityResource = locator.locateEntityResource(api,"cow", HttpMethod.GET); entityResource = locator.locateEntityResource(api,"cow", HttpMethod.GET);
result = executor.execute(entityResource, Params.valueOf((String)null, null, mock(WebScriptRequest.class)), response, true); result = executor.execute(entityResource, Params.valueOf((String)null, null, mock(WebScriptRequest.class)), response, true);
assertNotNull(result); assertNotNull(result);
verify(response, times(1)).setCache((Cache) ApiWebScript.CACHE_NEVER); verify(response, times(1)).setCache((Cache) ApiAssistant.CACHE_NEVER);
response = mock(WebScriptResponse.class); response = mock(WebScriptResponse.class);
result = executor.execute(entityResource, Params.valueOf("543", null, mock(WebScriptRequest.class)), response, true); result = executor.execute(entityResource, Params.valueOf("543", null, mock(WebScriptRequest.class)), response, true);
@@ -109,7 +110,7 @@ public class ExecutionTests extends AbstractContextTest
@Test @Test
public void testInvokePost() throws IOException public void testInvokePost() throws IOException
{ {
AbstractResourceWebScript executor = (AbstractResourceWebScript) applicationContext.getBean("executorOfPost"); AbstractResourceWebScript executor = getExecutor("executorOfPost");
ResourceWithMetadata resource = locator.locateRelationResource(api, "sheep", "blacksheep", HttpMethod.POST); ResourceWithMetadata resource = locator.locateRelationResource(api, "sheep", "blacksheep", HttpMethod.POST);
final Sheep aSheep = new Sheep("xyz"); final Sheep aSheep = new Sheep("xyz");
@@ -152,7 +153,7 @@ public class ExecutionTests extends AbstractContextTest
public void testInvokeDelete() throws IOException public void testInvokeDelete() throws IOException
{ {
ResourceWithMetadata grassResource = locator.locateEntityResource(api,"grass", HttpMethod.DELETE); ResourceWithMetadata grassResource = locator.locateEntityResource(api,"grass", HttpMethod.DELETE);
AbstractResourceWebScript executor = (AbstractResourceWebScript) applicationContext.getBean("executorOfDelete"); AbstractResourceWebScript executor = getExecutor("executorOfDelete");
Object result = executor.execute(grassResource, Params.valueOf("4", null, mock(WebScriptRequest.class)), mock(WebScriptResponse.class), false); Object result = executor.execute(grassResource, Params.valueOf("4", null, mock(WebScriptRequest.class)), mock(WebScriptResponse.class), false);
assertNull(result); assertNull(result);
@@ -190,7 +191,7 @@ public class ExecutionTests extends AbstractContextTest
public void testInvokePut() throws IOException public void testInvokePut() throws IOException
{ {
ResourceWithMetadata entityResource = locator.locateEntityResource(api,"sheep", HttpMethod.PUT); ResourceWithMetadata entityResource = locator.locateEntityResource(api,"sheep", HttpMethod.PUT);
AbstractResourceWebScript executor = (AbstractResourceWebScript) applicationContext.getBean("executorOfPut"); AbstractResourceWebScript executor = getExecutor("executorOfPut");
final Sheep aSheep = new Sheep("xyz"); final Sheep aSheep = new Sheep("xyz");
Object result = executor.execute(entityResource, Params.valueOf("654", null, NULL_PARAMS, aSheep, mock(WebScriptRequest.class)), mock(WebScriptResponse.class), false); Object result = executor.execute(entityResource, Params.valueOf("654", null, NULL_PARAMS, aSheep, mock(WebScriptRequest.class)), mock(WebScriptResponse.class), false);
assertNotNull(result); assertNotNull(result);
@@ -232,16 +233,13 @@ public class ExecutionTests extends AbstractContextTest
@Test @Test
public void testInvokeAbstract() throws IOException public void testInvokeAbstract() throws IOException
{ {
AbstractResourceWebScript executor = (AbstractResourceWebScript) applicationContext.getBean("executorOfGets"); AbstractResourceWebScript executor = getExecutor();
executor.setLocator(locator);
executor.setResolver(simpleMappingExceptionResolver);
executor.setJsonHelper(jsonHelper);
Map<String, String> templateVars = new HashMap(); Map<String, String> templateVars = new HashMap();
templateVars.put("apiScope", "private"); templateVars.put("apiScope", "private");
templateVars.put("apiVersion", "1"); templateVars.put("apiVersion", "1");
templateVars.put("apiName", "alfrescomock"); templateVars.put("apiName", "alfrescomock");
templateVars.put(ResourceLocator.COLLECTION_RESOURCE, "sheep"); templateVars.put(ResourceLocator.COLLECTION_RESOURCE, "sheep");
executor.execute(executor.determineApi(templateVars), mockRequest(templateVars,new HashMap<String, List<String>>(1)), mock(WebScriptResponse.class)); executor.execute(ApiAssistant.determineApi(templateVars), mockRequest(templateVars,new HashMap<String, List<String>>(1)), mock(WebScriptResponse.class));
WebScriptResponse response = mockResponse(); WebScriptResponse response = mockResponse();
templateVars.put(ResourceLocator.COLLECTION_RESOURCE, "bad"); templateVars.put(ResourceLocator.COLLECTION_RESOURCE, "bad");
@@ -260,10 +258,8 @@ public class ExecutionTests extends AbstractContextTest
@Test @Test
public void testInvalidUrls() throws IOException public void testInvalidUrls() throws IOException
{ {
AbstractResourceWebScript executor = (AbstractResourceWebScript) applicationContext.getBean("executorOfGets"); AbstractResourceWebScript executor = getExecutor();
executor.setLocator(locator);
executor.setResolver(simpleMappingExceptionResolver);
executor.setJsonHelper(jsonHelper);
Map<String, String> templateVars = new HashMap(); Map<String, String> templateVars = new HashMap();
templateVars.put("apiScope", "private"); templateVars.put("apiScope", "private");
templateVars.put("apiVersion", "1"); templateVars.put("apiVersion", "1");
@@ -279,13 +275,11 @@ public class ExecutionTests extends AbstractContextTest
@Test @Test
public void testRenderError() throws IOException public void testRenderError() throws IOException
{ {
AbstractResourceWebScript executor = (AbstractResourceWebScript) applicationContext.getBean("executorOfGets"); AbstractResourceWebScript executor = getExecutor();
executor.setResolver(simpleMappingExceptionResolver);
executor.setJsonHelper(jsonHelper);
ErrorResponse defaultError = new DefaultExceptionResolver().resolveException(new NullPointerException()); ErrorResponse defaultError = new DefaultExceptionResolver().resolveException(new NullPointerException());
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
executor.renderErrorResponse(defaultError, mockResponse(out)); apiAssistant.renderErrorResponse(defaultError, mockResponse(out));
String errorMessage = out.toString(); String errorMessage = out.toString();
// System.out.println(errorMessage); // System.out.println(errorMessage);
assertTrue(errorMessage.contains("\"errorKey\":\"framework.exception.ApiDefault\"")); assertTrue(errorMessage.contains("\"errorKey\":\"framework.exception.ApiDefault\""));
@@ -296,7 +290,7 @@ public class ExecutionTests extends AbstractContextTest
ErrorResponse anError = simpleMappingExceptionResolver.resolveException(new ApiException("nothing")); ErrorResponse anError = simpleMappingExceptionResolver.resolveException(new ApiException("nothing"));
out = new ByteArrayOutputStream(); out = new ByteArrayOutputStream();
executor.renderErrorResponse(anError, mockResponse(out)); apiAssistant.renderErrorResponse(anError, mockResponse(out));
errorMessage = out.toString(); errorMessage = out.toString();
// System.out.println(errorMessage); // System.out.println(errorMessage);
assertTrue(errorMessage.contains("\"errorKey\":\"nothing\"")); assertTrue(errorMessage.contains("\"errorKey\":\"nothing\""));
@@ -306,7 +300,7 @@ public class ExecutionTests extends AbstractContextTest
anError = simpleMappingExceptionResolver.resolveException(new EntityNotFoundException("2")); anError = simpleMappingExceptionResolver.resolveException(new EntityNotFoundException("2"));
out = new ByteArrayOutputStream(); out = new ByteArrayOutputStream();
executor.renderErrorResponse(anError, mockResponse(out)); apiAssistant.renderErrorResponse(anError, mockResponse(out));
errorMessage = out.toString(); errorMessage = out.toString();
System.out.println(errorMessage); System.out.println(errorMessage);
assertTrue(errorMessage.contains("\"errorKey\":\"framework.exception.EntityNotFound\"")); assertTrue(errorMessage.contains("\"errorKey\":\"framework.exception.EntityNotFound\""));

View File

@@ -51,6 +51,7 @@ import org.alfresco.rest.framework.tests.api.mocks3.GrassEntityResourceNowDelete
import org.alfresco.rest.framework.tests.api.mocks3.SheepBlackSheepResourceIsNoMore; import org.alfresco.rest.framework.tests.api.mocks3.SheepBlackSheepResourceIsNoMore;
import org.alfresco.rest.framework.tests.api.mocks3.SheepEntityResourceWithDeletedMethods; import org.alfresco.rest.framework.tests.api.mocks3.SheepEntityResourceWithDeletedMethods;
import org.alfresco.rest.framework.tests.api.mocks3.SlimGoat; import org.alfresco.rest.framework.tests.api.mocks3.SlimGoat;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.rest.framework.webscripts.ApiWebScript; import org.alfresco.rest.framework.webscripts.ApiWebScript;
import org.alfresco.rest.framework.webscripts.WithResponse; import org.alfresco.rest.framework.webscripts.WithResponse;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
@@ -440,7 +441,7 @@ public class InspectorTests
OperationResourceMetaData operationResourceMetaData = (OperationResourceMetaData) resourceMetadata; OperationResourceMetaData operationResourceMetaData = (OperationResourceMetaData) resourceMetadata;
Method actionMethod = operationResourceMetaData.getOperationMethod(); Method actionMethod = operationResourceMetaData.getOperationMethod();
String result = null; String result = null;
final WithResponse wr = new WithResponse(Status.STATUS_OK, ApiWebScript.DEFAULT_JSON_CONTENT, ApiWebScript.CACHE_NEVER); final WithResponse wr = new WithResponse(Status.STATUS_OK, ApiAssistant.DEFAULT_JSON_CONTENT, ApiAssistant.CACHE_NEVER);
switch (resourceMetadata.getUniqueId()) switch (resourceMetadata.getUniqueId())
{ {

View File

@@ -33,6 +33,7 @@ import org.alfresco.rest.framework.jacksonextensions.RestJsonModule;
import org.alfresco.rest.framework.resource.parameters.Paging; import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.rest.framework.resource.parameters.Params; import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.tests.api.mocks.Farmer; import org.alfresco.rest.framework.tests.api.mocks.Farmer;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.rest.framework.webscripts.ParamsExtractor; import org.alfresco.rest.framework.webscripts.ParamsExtractor;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptDelete; import org.alfresco.rest.framework.webscripts.ResourceWebScriptDelete;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptGet; import org.alfresco.rest.framework.webscripts.ResourceWebScriptGet;
@@ -57,6 +58,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
public class ParamsExtractorTests public class ParamsExtractorTests
{ {
static JacksonHelper jsonHelper = null; static JacksonHelper jsonHelper = null;
static ApiAssistant assistant = null;
@BeforeClass @BeforeClass
public static void setupTests() throws Exception public static void setupTests() throws Exception
@@ -65,6 +67,9 @@ public class ParamsExtractorTests
RestJsonModule module = new RestJsonModule(); RestJsonModule module = new RestJsonModule();
jsonHelper.setModule(module); jsonHelper.setModule(module);
jsonHelper.afterPropertiesSet(); jsonHelper.afterPropertiesSet();
assistant = new ApiAssistant();
assistant.setJsonHelper(jsonHelper);
} }
@Test @Test
@@ -132,7 +137,7 @@ public class ParamsExtractorTests
{ {
//Put together the stubs //Put together the stubs
ResourceWebScriptPost extractor = new ResourceWebScriptPost(); ResourceWebScriptPost extractor = new ResourceWebScriptPost();
extractor.setJsonHelper(jsonHelper); extractor.setAssistant(assistant);
Map<String, String> templateVars = new HashMap<String, String>(); Map<String, String> templateVars = new HashMap<String, String>();
Content content = mock(Content.class); Content content = mock(Content.class);
@@ -228,7 +233,7 @@ public class ParamsExtractorTests
public void testMultiPartPostExtractor() throws Exception public void testMultiPartPostExtractor() throws Exception
{ {
ResourceWebScriptPost extractor = new ResourceWebScriptPost(); ResourceWebScriptPost extractor = new ResourceWebScriptPost();
extractor.setJsonHelper(jsonHelper); extractor.setAssistant(assistant);
Map<String, String> templateVars = new HashMap<String, String>(); Map<String, String> templateVars = new HashMap<String, String>();
WebScriptRequest request = mock(WebScriptRequest.class); WebScriptRequest request = mock(WebScriptRequest.class);
@@ -285,7 +290,7 @@ public class ParamsExtractorTests
{ {
//Put together the stubs //Put together the stubs
ResourceWebScriptPut extractor = new ResourceWebScriptPut(); ResourceWebScriptPut extractor = new ResourceWebScriptPut();
extractor.setJsonHelper(jsonHelper); extractor.setAssistant(assistant);
Map<String, String> templateVars = new HashMap<String, String>(); Map<String, String> templateVars = new HashMap<String, String>();
Content content = mock(Content.class); Content content = mock(Content.class);
@@ -405,7 +410,7 @@ public class ParamsExtractorTests
{ {
String specialChars = new String(new char[] { (char) '香' }) + " 香蕉"; String specialChars = new String(new char[] { (char) '香' }) + " 香蕉";
ResourceWebScriptPost extractor = new ResourceWebScriptPost(); ResourceWebScriptPost extractor = new ResourceWebScriptPost();
extractor.setJsonHelper(jsonHelper); extractor.setAssistant(assistant);
Map<String, String> templateVars = new HashMap<String, String>(); Map<String, String> templateVars = new HashMap<String, String>();
String mockMe = "{\"name\":\""+specialChars+"\",\"created\":\"2012-03-23T15:56:18.552+0000\",\"age\":54,\"id\":\"1234A3\",\"farm\":\"LARGE\"}"; String mockMe = "{\"name\":\""+specialChars+"\",\"created\":\"2012-03-23T15:56:18.552+0000\",\"age\":54,\"id\":\"1234A3\",\"farm\":\"LARGE\"}";
Content content = mock(Content.class); Content content = mock(Content.class);
@@ -427,7 +432,7 @@ public class ParamsExtractorTests
//Test passing in special characters as a param. //Test passing in special characters as a param.
ResourceWebScriptGet getExtractor = new ResourceWebScriptGet(); ResourceWebScriptGet getExtractor = new ResourceWebScriptGet();
getExtractor.setJsonHelper(jsonHelper); getExtractor.setAssistant(assistant);
Map<String, String> getTemplateVars = new HashMap<String, String>(); Map<String, String> getTemplateVars = new HashMap<String, String>();
WebScriptRequest getRequest = mock(WebScriptRequest.class); WebScriptRequest getRequest = mock(WebScriptRequest.class);
when(getRequest.getServiceMatch()).thenReturn(new Match(null, getTemplateVars, null)); when(getRequest.getServiceMatch()).thenReturn(new Match(null, getTemplateVars, null));

View File

@@ -384,7 +384,7 @@ public class SerializeTests extends AbstractContextTest
{ {
Api api3 = Api.valueOf("alfrescomock", "private", "3"); Api api3 = Api.valueOf("alfrescomock", "private", "3");
ResourceWithMetadata propResource = locator.locateRelationResource(api3,"flock", "photo", HttpMethod.GET); ResourceWithMetadata propResource = locator.locateRelationResource(api3,"flock", "photo", HttpMethod.GET);
AbstractResourceWebScript executor = (AbstractResourceWebScript) applicationContext.getBean("executorOfGets"); AbstractResourceWebScript executor = getExecutor();
Object result = executor.execute(propResource, Params.valueOf("234", null, null), mock(WebScriptResponse.class), true); Object result = executor.execute(propResource, Params.valueOf("234", null, null), mock(WebScriptResponse.class), true);
assertNotNull(result); assertNotNull(result);
} }

View File

@@ -12,6 +12,7 @@ import static org.mockito.Mockito.when;
import org.alfresco.rest.framework.resource.content.ContentInfo; import org.alfresco.rest.framework.resource.content.ContentInfo;
import org.alfresco.rest.framework.resource.content.ContentInfoImpl; import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.rest.framework.webscripts.AbstractResourceWebScript; import org.alfresco.rest.framework.webscripts.AbstractResourceWebScript;
import org.alfresco.rest.framework.webscripts.ApiWebScript; import org.alfresco.rest.framework.webscripts.ApiWebScript;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptDelete; import org.alfresco.rest.framework.webscripts.ResourceWebScriptDelete;
@@ -43,17 +44,17 @@ public class WithResponseTest
@Test @Test
public void testDefaults() throws Exception public void testDefaults() throws Exception
{ {
WithResponse callBack = new WithResponse(Status.STATUS_OK,ApiWebScript.DEFAULT_JSON_CONTENT, ApiWebScript.CACHE_NEVER); WithResponse callBack = new WithResponse(Status.STATUS_OK,ApiAssistant.DEFAULT_JSON_CONTENT, ApiAssistant.CACHE_NEVER);
assertEquals(Status.STATUS_OK, callBack.getStatus()); assertEquals(Status.STATUS_OK, callBack.getStatus());
assertEquals(ApiWebScript.DEFAULT_JSON_CONTENT, callBack.getContentInfo()); assertEquals(ApiAssistant.DEFAULT_JSON_CONTENT, callBack.getContentInfo());
assertEquals(ApiWebScript.CACHE_NEVER, callBack.getCache()); assertEquals(ApiAssistant.CACHE_NEVER, callBack.getCache());
assertTrue(callBack.getHeaders().isEmpty()); assertTrue(callBack.getHeaders().isEmpty());
} }
@Test @Test
public void testSetHeader() throws Exception public void testSetHeader() throws Exception
{ {
WithResponse callBack = new WithResponse(Status.STATUS_OK,ApiWebScript.DEFAULT_JSON_CONTENT, ApiWebScript.CACHE_NEVER); WithResponse callBack = new WithResponse(Status.STATUS_OK,ApiAssistant.DEFAULT_JSON_CONTENT, ApiAssistant.CACHE_NEVER);
callBack.setHeader("king", "can"); callBack.setHeader("king", "can");
callBack.setHeader("king", "kong"); callBack.setHeader("king", "kong");
assertTrue(callBack.getHeaders().size() == 1); assertTrue(callBack.getHeaders().size() == 1);
@@ -65,7 +66,7 @@ public class WithResponseTest
@Test @Test
public void testAddHeader() throws Exception public void testAddHeader() throws Exception
{ {
WithResponse callBack = new WithResponse(Status.STATUS_OK,ApiWebScript.DEFAULT_JSON_CONTENT, ApiWebScript.CACHE_NEVER); WithResponse callBack = new WithResponse(Status.STATUS_OK,ApiAssistant.DEFAULT_JSON_CONTENT, ApiAssistant.CACHE_NEVER);
callBack.addHeader("king", "can"); callBack.addHeader("king", "can");
callBack.addHeader("king", "kong"); callBack.addHeader("king", "kong");
assertTrue(callBack.getHeaders().size() == 1); assertTrue(callBack.getHeaders().size() == 1);
@@ -78,7 +79,7 @@ public class WithResponseTest
@Test @Test
public void testSetters() throws Exception public void testSetters() throws Exception
{ {
WithResponse callBack = new WithResponse(Status.STATUS_OK, ApiWebScript.DEFAULT_JSON_CONTENT, ApiWebScript.CACHE_NEVER); WithResponse callBack = new WithResponse(Status.STATUS_OK, ApiAssistant.DEFAULT_JSON_CONTENT, ApiAssistant.CACHE_NEVER);
callBack.setStatus(Status.STATUS_GONE); callBack.setStatus(Status.STATUS_GONE);
Cache myCache = new Cache(new Description.RequiredCache() Cache myCache = new Cache(new Description.RequiredCache()
{ {
@@ -114,7 +115,8 @@ public class WithResponseTest
public void testSetResponse() throws Exception public void testSetResponse() throws Exception
{ {
AbstractResourceWebScript responseWriter = new ResourceWebScriptDelete(); AbstractResourceWebScript responseWriter = new ResourceWebScriptDelete();
WithResponse wr = new WithResponse(Status.STATUS_OK, ApiWebScript.DEFAULT_JSON_CONTENT, ApiWebScript.CACHE_NEVER); responseWriter.setAssistant(new ApiAssistant());
WithResponse wr = new WithResponse(Status.STATUS_OK, ApiAssistant.DEFAULT_JSON_CONTENT, ApiAssistant.CACHE_NEVER);
WebScriptResponse response = mock(WebScriptResponse.class); WebScriptResponse response = mock(WebScriptResponse.class);

View File

@@ -32,6 +32,10 @@
<bean id="jsonHelper" class="org.alfresco.rest.framework.jacksonextensions.JacksonHelper"> <bean id="jsonHelper" class="org.alfresco.rest.framework.jacksonextensions.JacksonHelper">
<property name="module" ref="restJsonModule" /> <property name="module" ref="restJsonModule" />
</bean> </bean>
<bean id="apiAssistant" class="org.alfresco.rest.framework.tools.ApiAssistant">
<property name="jsonHelper" ref="jsonHelper" />
<property name="resolver" ref="simpleMappingExceptionResolver" />
</bean>
<bean id="simpleMappingExceptionResolver" class="org.alfresco.rest.framework.core.exceptions.SimpleMappingExceptionResolver"> <bean id="simpleMappingExceptionResolver" class="org.alfresco.rest.framework.core.exceptions.SimpleMappingExceptionResolver">
<property name="exceptionMappings"> <property name="exceptionMappings">
<map> <map>
@@ -77,7 +81,6 @@
<bean id="defaultMetaWriter" class="org.alfresco.rest.framework.webscripts.metadata.WebScriptOptionsMetaData"> <bean id="defaultMetaWriter" class="org.alfresco.rest.framework.webscripts.metadata.WebScriptOptionsMetaData">
<property name="lookupDictionary" ref="apiLookup" /> <property name="lookupDictionary" ref="apiLookup" />
<property name="resolver" ref="simpleMappingExceptionResolver" /> <property name="assistant" ref="apiAssistant" />
<property name="jsonHelper" ref="jsonHelper" />
</bean> </bean>
</beans> </beans>