mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-14 17:58:59 +00:00
Merged searchapi (5.2.1) to 5.2.N (5.2.1)
129774 gjames: SEARCH-113: Moving more api logic to helper classes, using a "trait" style git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@130165 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -29,6 +29,7 @@ import java.io.IOException;
|
||||
|
||||
import org.alfresco.rest.framework.resource.SerializablePagedCollection;
|
||||
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
|
||||
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
|
||||
import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper;
|
||||
import org.codehaus.jackson.JsonGenerationException;
|
||||
import org.codehaus.jackson.JsonGenerator;
|
||||
@@ -90,8 +91,8 @@ public class SerializerOfCollectionWithPaging extends SerializerBase<Serializabl
|
||||
}
|
||||
if (pagedCol.getPaging() != null)
|
||||
{
|
||||
jgen.writeNumberField(ResourceWebScriptHelper.PARAM_PAGING_SKIP, pagedCol.getPaging().getSkipCount());
|
||||
jgen.writeNumberField(ResourceWebScriptHelper.PARAM_PAGING_MAX, pagedCol.getPaging().getMaxItems());
|
||||
jgen.writeNumberField(RecognizedParamsExtractor.PARAM_PAGING_SKIP, pagedCol.getPaging().getSkipCount());
|
||||
jgen.writeNumberField(RecognizedParamsExtractor.PARAM_PAGING_MAX, pagedCol.getPaging().getMaxItems());
|
||||
}
|
||||
jgen.writeEndObject();
|
||||
}
|
||||
|
@@ -56,34 +56,16 @@ 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<WebScriptException> webScriptExceptionResolver;
|
||||
private ExceptionResolver<Exception> resolver;
|
||||
private JacksonHelper jsonHelper;
|
||||
|
||||
/**
|
||||
* Determines the api being used from the templateVars
|
||||
* @param templateVars
|
||||
* @return Api
|
||||
*/
|
||||
public static Api determineApi(Map<String, String> templateVars)
|
||||
{
|
||||
String apiScope = templateVars.get("apiScope");
|
||||
@@ -92,6 +74,11 @@ public class ApiAssistant {
|
||||
return Api.valueOf(apiName,apiScope,apiVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves an exception as a json error.
|
||||
* @param exception
|
||||
* @return ErrorResponse
|
||||
*/
|
||||
public ErrorResponse resolveException(Exception ex)
|
||||
{
|
||||
ErrorResponse error = null;
|
||||
@@ -110,99 +97,6 @@ public class ApiAssistant {
|
||||
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;
|
||||
}
|
||||
|
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Remote API
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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/>.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
package org.alfresco.rest.framework.tools;
|
||||
|
||||
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
|
||||
import org.alfresco.rest.framework.jacksonextensions.BeanPropertiesFilter;
|
||||
import org.alfresco.rest.framework.resource.parameters.InvalidSelectException;
|
||||
import org.alfresco.rest.framework.resource.parameters.Paging;
|
||||
import org.alfresco.rest.framework.resource.parameters.Params;
|
||||
import org.alfresco.rest.framework.resource.parameters.SortColumn;
|
||||
import org.alfresco.rest.framework.resource.parameters.where.InvalidQueryException;
|
||||
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.WhereCompiler;
|
||||
import org.antlr.runtime.RecognitionException;
|
||||
import org.antlr.runtime.tree.CommonErrorNode;
|
||||
import org.antlr.runtime.tree.CommonTree;
|
||||
import org.antlr.runtime.tree.RewriteCardinalityException;
|
||||
import org.antlr.runtime.tree.Tree;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/*
|
||||
* Extracts recognized parameters from the HTTP request.
|
||||
*
|
||||
* @author Gethin James
|
||||
*/
|
||||
public interface RecognizedParamsExtractor
|
||||
{
|
||||
public static final String PARAM_RELATIONS = "relations";
|
||||
public static final String PARAM_FILTER_FIELDS = "fields";
|
||||
|
||||
@Deprecated
|
||||
public static final String PARAM_FILTER_PROPERTIES = "properties";
|
||||
|
||||
public static final String PARAM_PAGING_SKIP = "skipCount";
|
||||
public static final String PARAM_PAGING_MAX = "maxItems";
|
||||
public static final String PARAM_ORDERBY = "orderBy";
|
||||
public static final String PARAM_WHERE = "where";
|
||||
public static final String PARAM_SELECT = "select";
|
||||
public static final String PARAM_INCLUDE = "include";
|
||||
public static final String PARAM_INCLUDE_SOURCE_ENTITY = "includeSource";
|
||||
public static final List<String> KNOWN_PARAMS = Arrays.asList(
|
||||
PARAM_RELATIONS, PARAM_FILTER_PROPERTIES, PARAM_FILTER_FIELDS,PARAM_PAGING_SKIP,PARAM_PAGING_MAX,
|
||||
PARAM_ORDERBY, PARAM_WHERE, PARAM_SELECT, PARAM_INCLUDE_SOURCE_ENTITY);
|
||||
|
||||
default Log rpeLogger() {
|
||||
return LogFactory.getLog(this.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the formal set of params that any rest service could potentially have passed in as request params
|
||||
* @param req WebScriptRequest
|
||||
* @return RecognizedParams a POJO containing the params for use with the Params objects
|
||||
*/
|
||||
default Params.RecognizedParams getRecognizedParams(WebScriptRequest req)
|
||||
{
|
||||
Paging paging = findPaging(req);
|
||||
List<SortColumn> sorting = getSort(req.getParameter(PARAM_ORDERBY));
|
||||
Map<String, BeanPropertiesFilter> relationFilter = getRelationFilter(req.getParameter(PARAM_RELATIONS));
|
||||
Query whereQuery = getWhereClause(req.getParameter(PARAM_WHERE));
|
||||
Map<String, String[]> requestParams = getRequestParameters(req);
|
||||
boolean includeSource = Boolean.valueOf(req.getParameter(PARAM_INCLUDE_SOURCE_ENTITY));
|
||||
|
||||
List<String> includedFields = getIncludeClause(req.getParameter(PARAM_INCLUDE));
|
||||
List<String> selectFields = getSelectClause(req.getParameter(PARAM_SELECT));
|
||||
|
||||
String fields = req.getParameter(PARAM_FILTER_FIELDS);
|
||||
String properties = req.getParameter(PARAM_FILTER_PROPERTIES);
|
||||
|
||||
if ((fields != null) && (properties != null))
|
||||
{
|
||||
if (rpeLogger().isWarnEnabled())
|
||||
{
|
||||
rpeLogger().warn("Taking 'fields' param [" + fields + "] and ignoring deprecated 'properties' param [" + properties + "]");
|
||||
}
|
||||
}
|
||||
|
||||
BeanPropertiesFilter filter = getFilter((fields != null ? fields : properties), includedFields);
|
||||
|
||||
return new Params.RecognizedParams(requestParams, paging, filter, relationFilter, includedFields, selectFields, whereQuery, sorting, includeSource);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Takes the web request and looks for a "fields" parameter (otherwise deprecated "properties" parameter).
|
||||
*
|
||||
* Parses the parameter and produces a list of bean properties to use as a filter A
|
||||
* SimpleBeanPropertyFilter it returned that uses the bean properties. If no
|
||||
* filter param is set then a default BeanFilter is returned that will never
|
||||
* filter fields (ie. Returns all bean properties).
|
||||
*
|
||||
* If selectList is provided then it will take precedence (ie. be included) over the fields/properties filter
|
||||
* for top-level entries (bean properties).
|
||||
*
|
||||
* For example, this will return entries from both select & properties, eg.
|
||||
*
|
||||
* select=abc,def&properties=id,name,ghi
|
||||
*
|
||||
* Note: it should be noted that API-generic "fields" clause does not currently work for sub-entries.
|
||||
*
|
||||
* Hence, even if the API-specific "select" clause allows selection of a sub-entries this cannot be used
|
||||
* with "fields" filtering. For example, an API-specific method may implement and return "abc/blah", eg.
|
||||
*
|
||||
* select=abc/blah
|
||||
*
|
||||
* However the following will not return "abc/blah" if used with fields filtering, eg.
|
||||
*
|
||||
* select=abc/blah&fields=id,name,ghi
|
||||
*
|
||||
* If fields filtering is desired then it would require "abc" to be selected and returned as a whole, eg.
|
||||
*
|
||||
* select=abc&fields=id,name,ghi
|
||||
*
|
||||
* @param filterParams
|
||||
* @param selectList
|
||||
* @return
|
||||
*/
|
||||
default BeanPropertiesFilter getFilter(String filterParams, List<String> selectList)
|
||||
{
|
||||
if (filterParams != null)
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(filterParams, ",");
|
||||
Set<String> filteredProperties = new HashSet<String>(st.countTokens());
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
filteredProperties.add(st.nextToken());
|
||||
}
|
||||
|
||||
// if supplied, the select takes precedence over the filter (fields/properties) for top-level bean properties
|
||||
if (selectList != null)
|
||||
{
|
||||
for (String select : selectList)
|
||||
{
|
||||
String[] split = select.split("/");
|
||||
filteredProperties.add(split[0]);
|
||||
}
|
||||
}
|
||||
|
||||
rpeLogger().debug("Filtering using the following properties: " + filteredProperties);
|
||||
BeanPropertiesFilter filter = new BeanPropertiesFilter(filteredProperties);
|
||||
return filter;
|
||||
}
|
||||
return BeanPropertiesFilter.ALLOW_ALL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Takes the "select" parameter and turns it into a List<String> property names
|
||||
* @param selectParam String
|
||||
* @return bean property names potentially using JSON Pointer syntax
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Deprecated
|
||||
default List<String> getSelectClause(String selectParam) throws InvalidArgumentException
|
||||
{
|
||||
return getClause(selectParam, "SELECT");
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the "include" parameter and turns it into a List<String> property names
|
||||
* @param includeParam String
|
||||
* @return bean property names potentially using JSON Pointer syntax
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default List<String> getIncludeClause(String includeParam) throws InvalidArgumentException
|
||||
{
|
||||
return getClause(includeParam, "INCLUDE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the clause specificed in paramName
|
||||
* @param param
|
||||
* @param paramName
|
||||
* @return bean property names potentially using JSON Pointer syntax
|
||||
*/
|
||||
default List<String> getClause(String param, String paramName)
|
||||
{
|
||||
if (param == null) return Collections.emptyList();
|
||||
|
||||
try {
|
||||
CommonTree selectedPropsTree = WhereCompiler.compileSelectClause(param);
|
||||
if (selectedPropsTree instanceof CommonErrorNode)
|
||||
{
|
||||
rpeLogger().debug("Error parsing the "+paramName+" clause "+selectedPropsTree);
|
||||
throw new InvalidSelectException(paramName, selectedPropsTree);
|
||||
}
|
||||
if (selectedPropsTree.getChildCount() == 0 && !selectedPropsTree.getText().isEmpty())
|
||||
{
|
||||
return Arrays.asList(selectedPropsTree.getText());
|
||||
}
|
||||
List<Tree> children = (List<Tree>) selectedPropsTree.getChildren();
|
||||
if (children!= null && !children.isEmpty())
|
||||
{
|
||||
List<String> properties = new ArrayList<String>(children.size());
|
||||
for (Tree child : children) {
|
||||
properties.add(child.getText());
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
catch (RewriteCardinalityException re)
|
||||
{
|
||||
//Catch any error so it doesn't get thrown up the stack
|
||||
rpeLogger().debug("Unhandled Error parsing the "+paramName+" clause: "+re);
|
||||
}
|
||||
catch (RecognitionException e)
|
||||
{
|
||||
rpeLogger().debug("Error parsing the \"+paramName+\" clause: "+param);
|
||||
}
|
||||
catch (InvalidQueryException iqe)
|
||||
{
|
||||
throw new InvalidSelectException(paramName, iqe.getQueryParam());
|
||||
}
|
||||
//Default to throw out an invalid query
|
||||
throw new InvalidSelectException(paramName, param);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the "where" parameter and turns it into a Java Object that can be used for querying
|
||||
* @param whereParam String
|
||||
* @return Query a parsed version of the where clause, represented in Java
|
||||
*/
|
||||
default Query getWhereClause(String whereParam) throws InvalidQueryException
|
||||
{
|
||||
if (whereParam == null) return QueryImpl.EMPTY;
|
||||
|
||||
try {
|
||||
CommonTree whereTree = WhereCompiler.compileWhereClause(whereParam);
|
||||
if (whereTree instanceof CommonErrorNode)
|
||||
{
|
||||
rpeLogger().debug("Error parsing the WHERE clause "+whereTree);
|
||||
throw new InvalidQueryException(whereTree);
|
||||
}
|
||||
return new QueryImpl(whereTree);
|
||||
} catch (RewriteCardinalityException re) { //Catch any error so it doesn't get thrown up the stack
|
||||
rpeLogger().info("Unhandled Error parsing the WHERE clause: "+re);
|
||||
} catch (RecognitionException e) {
|
||||
whereParam += ", "+WhereCompiler.resolveMessage(e);
|
||||
rpeLogger().info("Error parsing the WHERE clause: "+whereParam);
|
||||
}
|
||||
//Default to throw out an invalid query
|
||||
throw new InvalidQueryException(whereParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the Sort parameter as a String and parses it into a List of SortColumn objects.
|
||||
* The format is a comma seperated list of "columnName sortDirection",
|
||||
* e.g. "name DESC, age ASC". It is not case sensitive and the sort direction is optional
|
||||
* It default to sort ASCENDING.
|
||||
* @param sortParams - String passed in on the request
|
||||
* @return - the sort columns or an empty list if the params were invalid.
|
||||
*/
|
||||
default List<SortColumn> getSort(String sortParams)
|
||||
{
|
||||
if (sortParams != null)
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(sortParams, ",");
|
||||
List<SortColumn> sortedColumns = new ArrayList<SortColumn>(st.countTokens());
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
String token = st.nextToken();
|
||||
StringTokenizer columnDesc = new StringTokenizer(token, " ");
|
||||
if (columnDesc.countTokens() <= 2)
|
||||
{
|
||||
String columnName = columnDesc.nextToken();
|
||||
String sortOrder = SortColumn.ASCENDING;
|
||||
if (columnDesc.hasMoreTokens())
|
||||
{
|
||||
String sortDef = columnDesc.nextToken().toUpperCase();
|
||||
if (SortColumn.ASCENDING.equals(sortDef) || SortColumn.DESCENDING.equals(sortDef))
|
||||
{
|
||||
sortOrder = sortDef;
|
||||
}
|
||||
else
|
||||
{
|
||||
rpeLogger().debug("Invalid sort order definition ("+sortDef+"). Valid values are "+SortColumn.ASCENDING+" or "+SortColumn.DESCENDING+".");
|
||||
}
|
||||
}
|
||||
sortedColumns.add(new SortColumn(columnName, SortColumn.ASCENDING.equals(sortOrder)));
|
||||
}
|
||||
// filteredProperties.add();
|
||||
}
|
||||
// logger.debug("Filtering using the following properties: " + filteredProperties);
|
||||
// BeanPropertiesFilter filter = new BeanPropertiesFilter(filteredProperties);
|
||||
return sortedColumns;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find paging setings based on the request parameters.
|
||||
*
|
||||
* @param req
|
||||
* @return Paging
|
||||
*/
|
||||
default Paging findPaging(WebScriptRequest req)
|
||||
{
|
||||
int skipped = Paging.DEFAULT_SKIP_COUNT;
|
||||
int max = Paging.DEFAULT_MAX_ITEMS;
|
||||
String skip = req.getParameter(PARAM_PAGING_SKIP);
|
||||
String maxItems = req.getParameter(PARAM_PAGING_MAX);
|
||||
|
||||
try
|
||||
{
|
||||
if (skip != null) { skipped = Integer.parseInt(skip);}
|
||||
if (maxItems != null) { max = Integer.parseInt(maxItems); }
|
||||
if (max < 0 || skipped < 0)
|
||||
{
|
||||
throw new InvalidArgumentException("Negative values not supported.");
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException error)
|
||||
{
|
||||
if (rpeLogger().isDebugEnabled())
|
||||
{
|
||||
rpeLogger().debug("Invalid paging params skip: " + skip + ",maxItems:" + maxItems);
|
||||
}
|
||||
throw new InvalidArgumentException();
|
||||
}
|
||||
|
||||
return Paging.valueOf(skipped, max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Takes the web request and looks for a "fields" parameter (otherwise deprecated "properties" parameter).
|
||||
*
|
||||
* Parses the parameter and produces a list of bean properties to use as a filter A
|
||||
* SimpleBeanPropertyFilter it returned that uses the bean properties. If no
|
||||
* filter param is set then a default BeanFilter is returned that will never
|
||||
* filter fields (ie. Returns all bean properties).
|
||||
*
|
||||
* @param filterParams String
|
||||
* @return BeanPropertyFilter - if no parameter then returns a new
|
||||
* ReturnAllBeanProperties class
|
||||
*/
|
||||
default BeanPropertiesFilter getFilter(String filterParams)
|
||||
{
|
||||
return getFilter(filterParams, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the web request and looks for a "relations" parameter Parses the
|
||||
* parameter and produces a list of bean properties to use as a filter A
|
||||
* SimpleBeanPropertiesFilter it returned that uses the properties If no
|
||||
* filter param is set then a default BeanFilter is returned that will never
|
||||
* filter properties (ie. Returns all bean properties).
|
||||
*
|
||||
* @param filterParams String
|
||||
* @return BeanPropertiesFilter - if no parameter then returns a new
|
||||
* ReturnAllBeanProperties class
|
||||
*/
|
||||
default Map<String, BeanPropertiesFilter> getRelationFilter(String filterParams)
|
||||
{
|
||||
if (filterParams != null)
|
||||
{
|
||||
// Split by a comma when not in a bracket
|
||||
String[] relations = filterParams.split(",(?![^()]*+\\))");
|
||||
Map<String, BeanPropertiesFilter> filterMap = new HashMap<String, BeanPropertiesFilter>(relations.length);
|
||||
|
||||
for (String relation : relations)
|
||||
{
|
||||
int bracketLocation = relation.indexOf("(");
|
||||
if (bracketLocation != -1)
|
||||
{
|
||||
// We have properties
|
||||
String relationKey = relation.substring(0, bracketLocation);
|
||||
String props = relation.substring(bracketLocation + 1, relation.length() - 1);
|
||||
filterMap.put(relationKey, getFilter(props));
|
||||
}
|
||||
else
|
||||
{
|
||||
// no properties so just get the String
|
||||
filterMap.put(relation, getFilter(null));
|
||||
}
|
||||
}
|
||||
return filterMap;
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds all request parameters that aren't already know about (eg. not paging or filter params)
|
||||
* and returns them for use.
|
||||
*
|
||||
* @param req - the WebScriptRequest object
|
||||
* @return the request parameters
|
||||
*/
|
||||
default Map<String, String[]> getRequestParameters(WebScriptRequest req)
|
||||
{
|
||||
if (req!= null)
|
||||
{
|
||||
String[] paramNames = req.getParameterNames();
|
||||
if (paramNames!= null)
|
||||
{
|
||||
Map<String, String[]> requestParameteters = new HashMap<String, String[]>(paramNames.length);
|
||||
|
||||
for (int i = 0; i < paramNames.length; i++)
|
||||
{
|
||||
String paramName = paramNames[i];
|
||||
if (!KNOWN_PARAMS.contains(paramName))
|
||||
{
|
||||
String[] vals = req.getParameterValues(paramName);
|
||||
requestParameteters.put(paramName, vals);
|
||||
}
|
||||
}
|
||||
return requestParameteters;
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
}
|
103
source/java/org/alfresco/rest/framework/tools/RequestReader.java
Normal file
103
source/java/org/alfresco/rest/framework/tools/RequestReader.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Remote API
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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/>.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
package org.alfresco.rest.framework.tools;
|
||||
|
||||
import org.alfresco.rest.framework.core.exceptions.ApiException;
|
||||
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
|
||||
import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.codehaus.jackson.map.JsonMappingException;
|
||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* Reads information from the request
|
||||
*
|
||||
* @author Gethin James
|
||||
*/
|
||||
public interface RequestReader
|
||||
{
|
||||
/**
|
||||
* Extracts the body contents from the request
|
||||
*
|
||||
* @param req the request
|
||||
* @param jsonHelper Jackson Helper
|
||||
* @param requiredType the type to return
|
||||
* @return the Object in the required type
|
||||
*/
|
||||
default <T> T extractJsonContent(WebScriptRequest req, JacksonHelper jsonHelper, Class<T> requiredType)
|
||||
{
|
||||
Reader reader;
|
||||
try
|
||||
{
|
||||
reader = req.getContent().getReader();
|
||||
return jsonHelper.construct(reader, requiredType);
|
||||
}
|
||||
catch (JsonMappingException e)
|
||||
{
|
||||
rrLogger().warn("Could not read content from HTTP request body.", e);
|
||||
throw new InvalidArgumentException("Could not read content from HTTP request body.");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ApiException("Could not read content from HTTP request body.", e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the body contents from the request as a List, the JSON can be an array or just a single value without the [] symbols
|
||||
*
|
||||
* @param req the request
|
||||
* @param jsonHelper Jackson Helper
|
||||
* @param requiredType the type to return (without the List param)
|
||||
* @return A List of "Object" as the required type
|
||||
*/
|
||||
default <T> List<T> extractJsonContentAsList(WebScriptRequest req, JacksonHelper jsonHelper, Class<T> requiredType)
|
||||
{
|
||||
Reader reader;
|
||||
try
|
||||
{
|
||||
reader = req.getContent().getReader();
|
||||
return jsonHelper.constructList(reader, requiredType);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ApiException("Could not read content from HTTP request body.", e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
default Log rrLogger() {
|
||||
return LogFactory.getLog(this.getClass());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Remote API
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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/>.
|
||||
* #L%
|
||||
*/
|
||||
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.UnsupportedResourceOperationException;
|
||||
import org.alfresco.rest.framework.jacksonextensions.BeanPropertiesFilter;
|
||||
import org.alfresco.rest.framework.jacksonextensions.ExecutionResult;
|
||||
import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
|
||||
import org.alfresco.rest.framework.resource.SerializablePagedCollection;
|
||||
import org.alfresco.rest.framework.resource.content.BinaryResource;
|
||||
import org.alfresco.rest.framework.resource.content.ContentInfo;
|
||||
import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
|
||||
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
|
||||
import org.alfresco.rest.framework.resource.parameters.Params;
|
||||
import org.alfresco.rest.framework.webscripts.WithResponse;
|
||||
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.beans.BeanUtils;
|
||||
import org.springframework.extensions.surf.util.I18NUtil;
|
||||
import org.springframework.extensions.webscripts.*;
|
||||
import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* Writes to the response
|
||||
*
|
||||
* @author Gethin James
|
||||
*/
|
||||
public interface ResponseWriter
|
||||
{
|
||||
|
||||
String UTF8 = "UTF-8";
|
||||
ContentInfo DEFAULT_JSON_CONTENT = new ContentInfoImpl(Format.JSON.mimetype(),UTF8, 0, null);
|
||||
Cache CACHE_NEVER = new Cache(new Description.RequiredCache()
|
||||
{
|
||||
@Override
|
||||
public boolean getNeverCache()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getIsPublic()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getMustRevalidate()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
default Log resWriterLogger() {
|
||||
return LogFactory.getLog(this.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the response headers with any information we know about the content
|
||||
* @param res WebScriptResponse
|
||||
* @param contentInfo Content Information
|
||||
*/
|
||||
default 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The response 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.
|
||||
*
|
||||
* If you set content information via the contentInfo object and ALSO the headers then "headers" will win because they are
|
||||
* set last.
|
||||
*
|
||||
* @param res
|
||||
* @param status
|
||||
* @param cache
|
||||
* @param contentInfo
|
||||
* @param headers
|
||||
*/
|
||||
default void setResponse(final WebScriptResponse res, int status, Cache cache, ContentInfo contentInfo, Map<String, List<String>> headers)
|
||||
{
|
||||
res.setStatus(status);
|
||||
if (cache != null) res.setCache(cache);
|
||||
setContentInfoOnResponse(res,contentInfo);
|
||||
if (headers != null && !headers.isEmpty())
|
||||
{
|
||||
for (Map.Entry<String, List<String>> header:headers.entrySet())
|
||||
{
|
||||
for (int i=0; i < header.getValue().size(); i++) {
|
||||
if (i==0)
|
||||
{
|
||||
//If its the first one then set the header overwriting.
|
||||
res.setHeader(header.getKey(), header.getValue().get(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
//If its not the first one than update the header
|
||||
res.addHeader(header.getKey(), header.getValue().get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the response using the WithResponse object
|
||||
* @param res
|
||||
* @param withResponse
|
||||
*/
|
||||
default void setResponse(final WebScriptResponse res, WithResponse withResponse)
|
||||
{
|
||||
setResponse(res, withResponse.getStatus(), withResponse.getCache(), withResponse.getContentInfo(), withResponse.getHeaders());
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a JSON error response
|
||||
* @param errorResponse The error
|
||||
* @param res web script response
|
||||
* @throws IOException
|
||||
*/
|
||||
default void renderErrorResponse(final ErrorResponse errorResponse, final WebScriptResponse res, final JacksonHelper jsonHelper) throws IOException {
|
||||
|
||||
String logId = "";
|
||||
|
||||
if (Status.STATUS_INTERNAL_SERVER_ERROR == errorResponse.getStatusCode() || resWriterLogger().isDebugEnabled())
|
||||
{
|
||||
logId = org.alfresco.util.GUID.generate();
|
||||
resWriterLogger().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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders an exception to the output stream as Json.
|
||||
* @param exception
|
||||
* @param response
|
||||
* @throws IOException
|
||||
*/
|
||||
default void renderException(final Exception exception, final WebScriptResponse response, final ApiAssistant assistant) throws IOException {
|
||||
renderErrorResponse(assistant.resolveException(exception), response, assistant.getJsonHelper());
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the result of an execution.
|
||||
*
|
||||
* @param res WebScriptResponse
|
||||
* @param toSerialize result of an execution
|
||||
* @throws IOException
|
||||
*/
|
||||
default void renderJsonResponse(final WebScriptResponse res, final Object toSerialize, final JacksonHelper jsonHelper)
|
||||
throws IOException
|
||||
{
|
||||
jsonHelper.withWriter(res.getOutputStream(), new JacksonHelper.Writer()
|
||||
{
|
||||
@Override
|
||||
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
|
||||
throws JsonGenerationException, JsonMappingException, IOException
|
||||
{
|
||||
objectMapper.writeValue(generator, toSerialize);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@@ -53,6 +53,7 @@ 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.alfresco.rest.framework.tools.ResponseWriter;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -80,7 +81,7 @@ import org.springframework.http.HttpMethod;
|
||||
*/
|
||||
// TODO for requests that pass in input streams e.g. binary content for workflow, this is going to need a way to re-read the input stream a la
|
||||
// code in RepositoryContainer due to retrying transaction logic
|
||||
public abstract class AbstractResourceWebScript extends ApiWebScript implements HttpMethodSupport, ActionExecutor
|
||||
public abstract class AbstractResourceWebScript extends ApiWebScript implements HttpMethodSupport, ActionExecutor, ResponseWriter
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(AbstractResourceWebScript.class);
|
||||
|
||||
@@ -146,18 +147,18 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
|
||||
}
|
||||
else
|
||||
{
|
||||
renderJsonResponse(res, toSerialize);
|
||||
renderJsonResponse(res, toSerialize, assistant.getJsonHelper());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (AlfrescoRuntimeException | ApiException | WebScriptException xception )
|
||||
{
|
||||
assistant.renderException(xception, res);
|
||||
renderException(xception, res, assistant);
|
||||
}
|
||||
catch (RuntimeException runtimeException)
|
||||
{
|
||||
assistant.renderException(runtimeException, res);
|
||||
renderException(runtimeException, res, assistant);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +166,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(), ApiAssistant.DEFAULT_JSON_CONTENT,ApiAssistant.CACHE_NEVER);
|
||||
final WithResponse callBack = new WithResponse(operation.getSuccessStatus(), DEFAULT_JSON_CONTENT,CACHE_NEVER);
|
||||
Object toReturn = transactionService.getRetryingTransactionHelper().doInTransaction(
|
||||
new RetryingTransactionHelper.RetryingTransactionCallback<Object>()
|
||||
{
|
||||
@@ -199,7 +200,7 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
|
||||
{
|
||||
NodeBinaryResource nodeResource = (NodeBinaryResource) resource;
|
||||
ContentInfo contentInfo = nodeResource.getContentInfo();
|
||||
assistant.setContentInfoOnResponse(res, contentInfo);
|
||||
setContentInfoOnResponse(res, contentInfo);
|
||||
// if requested, set attachment
|
||||
boolean attach = StringUtils.isNotEmpty(nodeResource.getAttachFileName());
|
||||
Map<String, Object> model = getModelForCacheDirective(nodeResource.getCacheDirective());
|
||||
@@ -217,70 +218,6 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The response 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.
|
||||
*
|
||||
* If you set content information via the contentInfo object and ALSO the headers then "headers" will win because they are
|
||||
* set last.
|
||||
*
|
||||
* @param res
|
||||
* @param status
|
||||
* @param cache
|
||||
* @param contentInfo
|
||||
* @param headers
|
||||
*/
|
||||
public void setResponse(final WebScriptResponse res, int status, Cache cache, ContentInfo contentInfo, Map<String, List<String>> headers)
|
||||
{
|
||||
res.setStatus(status);
|
||||
if (cache != null) res.setCache(cache);
|
||||
assistant.setContentInfoOnResponse(res,contentInfo);
|
||||
if (headers != null && !headers.isEmpty())
|
||||
{
|
||||
for (Map.Entry<String, List<String>> header:headers.entrySet())
|
||||
{
|
||||
for (int i=0; i < header.getValue().size(); i++) {
|
||||
if (i==0)
|
||||
{
|
||||
//If its the first one then set the header overwriting.
|
||||
res.setHeader(header.getKey(), header.getValue().get(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
//If its not the first one than update the header
|
||||
res.addHeader(header.getKey(), header.getValue().get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setResponse(final WebScriptResponse res, WithResponse withResponse)
|
||||
{
|
||||
setResponse(res, withResponse.getStatus(), withResponse.getCache(), withResponse.getContentInfo(), withResponse.getHeaders());
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the result of an execution.
|
||||
*
|
||||
* @param res WebScriptResponse
|
||||
* @param toSerialize result of an execution
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void renderJsonResponse(final WebScriptResponse res, final Object toSerialize)
|
||||
throws IOException
|
||||
{
|
||||
assistant.getJsonHelper().withWriter(res.getOutputStream(), new JacksonHelper.Writer()
|
||||
{
|
||||
@Override
|
||||
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
|
||||
throws JsonGenerationException, JsonMappingException, IOException
|
||||
{
|
||||
objectMapper.writeValue(generator, toSerialize);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setLocator(ResourceLocator locator)
|
||||
{
|
||||
this.locator = locator;
|
||||
|
@@ -38,6 +38,7 @@ import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResou
|
||||
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
|
||||
import org.alfresco.rest.framework.resource.parameters.Params;
|
||||
import org.alfresco.rest.framework.tools.ApiAssistant;
|
||||
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||
import org.springframework.extensions.webscripts.WebScriptResponse;
|
||||
@@ -48,7 +49,7 @@ import org.springframework.http.HttpMethod;
|
||||
*
|
||||
* @author Gethin James
|
||||
*/
|
||||
public class ResourceWebScriptDelete extends AbstractResourceWebScript implements ParamsExtractor
|
||||
public class ResourceWebScriptDelete extends AbstractResourceWebScript implements ParamsExtractor, RecognizedParamsExtractor
|
||||
{
|
||||
|
||||
public ResourceWebScriptDelete()
|
||||
@@ -63,7 +64,7 @@ public class ResourceWebScriptDelete extends AbstractResourceWebScript implement
|
||||
{
|
||||
String entityId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.ENTITY_ID);
|
||||
String relationshipId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.RELATIONSHIP_ID);
|
||||
final Params.RecognizedParams params = ResourceWebScriptHelper.getRecognizedParams(req);
|
||||
final Params.RecognizedParams params = getRecognizedParams(req);
|
||||
|
||||
switch (resourceMeta.getType())
|
||||
{
|
||||
@@ -215,7 +216,7 @@ public class ResourceWebScriptDelete extends AbstractResourceWebScript implement
|
||||
public Void execute(final ResourceWithMetadata resource, final Params params, final WebScriptResponse res, boolean isReadOnly)
|
||||
{
|
||||
final ResourceOperation operation = resource.getMetaData().getOperation(HttpMethod.DELETE);
|
||||
final WithResponse callBack = new WithResponse(operation.getSuccessStatus(), ApiAssistant.DEFAULT_JSON_CONTENT,ApiAssistant.CACHE_NEVER);
|
||||
final WithResponse callBack = new WithResponse(operation.getSuccessStatus(), DEFAULT_JSON_CONTENT,CACHE_NEVER);
|
||||
transactionService.getRetryingTransactionHelper().doInTransaction(
|
||||
new RetryingTransactionCallback<Void>()
|
||||
{
|
||||
|
@@ -40,6 +40,7 @@ import org.alfresco.rest.framework.resource.content.BinaryResource;
|
||||
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
|
||||
import org.alfresco.rest.framework.resource.parameters.Params;
|
||||
import org.alfresco.rest.framework.resource.parameters.Params.RecognizedParams;
|
||||
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -51,7 +52,7 @@ import org.springframework.http.HttpMethod;
|
||||
*
|
||||
* @author Gethin James
|
||||
*/
|
||||
public class ResourceWebScriptGet extends AbstractResourceWebScript implements ParamsExtractor
|
||||
public class ResourceWebScriptGet extends AbstractResourceWebScript implements ParamsExtractor, RecognizedParamsExtractor
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(ResourceWebScriptGet.class);
|
||||
|
||||
@@ -67,7 +68,7 @@ public class ResourceWebScriptGet extends AbstractResourceWebScript implements P
|
||||
{
|
||||
final String entityId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.ENTITY_ID);
|
||||
final String relationshipId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.RELATIONSHIP_ID);
|
||||
final RecognizedParams params = ResourceWebScriptHelper.getRecognizedParams(req);
|
||||
final RecognizedParams params = getRecognizedParams(req);
|
||||
|
||||
switch (resourceMeta.getType())
|
||||
{
|
||||
|
@@ -67,6 +67,7 @@ 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.WhereCompiler;
|
||||
import org.alfresco.rest.framework.tools.ApiAssistant;
|
||||
import org.alfresco.rest.framework.tools.ResponseWriter;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.alfresco.util.PropertyCheck;
|
||||
import org.antlr.runtime.RecognitionException;
|
||||
@@ -92,336 +93,10 @@ import org.springframework.http.HttpMethod;
|
||||
public class ResourceWebScriptHelper
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(ResourceWebScriptHelper.class);
|
||||
public static final String PARAM_RELATIONS = "relations";
|
||||
|
||||
public static final String PARAM_FILTER_FIELDS = "fields";
|
||||
|
||||
@Deprecated
|
||||
public static final String PARAM_FILTER_PROPERTIES = "properties";
|
||||
|
||||
public static final String PARAM_PAGING_SKIP = "skipCount";
|
||||
public static final String PARAM_PAGING_MAX = "maxItems";
|
||||
public static final String PARAM_ORDERBY = "orderBy";
|
||||
public static final String PARAM_WHERE = "where";
|
||||
|
||||
public static final String PARAM_SELECT = "select";
|
||||
|
||||
public static final String PARAM_INCLUDE = "include";
|
||||
public static final String PARAM_INCLUDE_SOURCE_ENTITY = "includeSource";
|
||||
|
||||
public static final List<String> KNOWN_PARAMS = Arrays.asList(
|
||||
PARAM_RELATIONS, PARAM_FILTER_PROPERTIES, PARAM_FILTER_FIELDS,PARAM_PAGING_SKIP,PARAM_PAGING_MAX,
|
||||
PARAM_ORDERBY, PARAM_WHERE, PARAM_SELECT, PARAM_INCLUDE_SOURCE_ENTITY);
|
||||
|
||||
private ResourceLocator locator;
|
||||
|
||||
private ActionExecutor executor;
|
||||
|
||||
/**
|
||||
* Takes the web request and looks for a "fields" parameter (otherwise deprecated "properties" parameter).
|
||||
*
|
||||
* Parses the parameter and produces a list of bean properties to use as a filter A
|
||||
* SimpleBeanPropertyFilter it returned that uses the bean properties. If no
|
||||
* filter param is set then a default BeanFilter is returned that will never
|
||||
* filter fields (ie. Returns all bean properties).
|
||||
*
|
||||
* @param filterParams String
|
||||
* @return BeanPropertyFilter - if no parameter then returns a new
|
||||
* ReturnAllBeanProperties class
|
||||
*/
|
||||
public static BeanPropertiesFilter getFilter(String filterParams)
|
||||
{
|
||||
return getFilter(filterParams, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the web request and looks for a "fields" parameter (otherwise deprecated "properties" parameter).
|
||||
*
|
||||
* Parses the parameter and produces a list of bean properties to use as a filter A
|
||||
* SimpleBeanPropertyFilter it returned that uses the bean properties. If no
|
||||
* filter param is set then a default BeanFilter is returned that will never
|
||||
* filter fields (ie. Returns all bean properties).
|
||||
*
|
||||
* If selectList is provided then it will take precedence (ie. be included) over the fields/properties filter
|
||||
* for top-level entries (bean properties).
|
||||
*
|
||||
* For example, this will return entries from both select & properties, eg.
|
||||
*
|
||||
* select=abc,def&properties=id,name,ghi
|
||||
*
|
||||
* Note: it should be noted that API-generic "fields" clause does not currently work for sub-entries.
|
||||
*
|
||||
* Hence, even if the API-specific "select" clause allows selection of a sub-entries this cannot be used
|
||||
* with "fields" filtering. For example, an API-specific method may implement and return "abc/blah", eg.
|
||||
*
|
||||
* select=abc/blah
|
||||
*
|
||||
* However the following will not return "abc/blah" if used with fields filtering, eg.
|
||||
*
|
||||
* select=abc/blah&fields=id,name,ghi
|
||||
*
|
||||
* If fields filtering is desired then it would require "abc" to be selected and returned as a whole, eg.
|
||||
*
|
||||
* select=abc&fields=id,name,ghi
|
||||
*
|
||||
* @param filterParams
|
||||
* @param selectList
|
||||
* @return
|
||||
*/
|
||||
public static BeanPropertiesFilter getFilter(String filterParams, List<String> selectList)
|
||||
{
|
||||
if (filterParams != null)
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(filterParams, ",");
|
||||
Set<String> filteredProperties = new HashSet<String>(st.countTokens());
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
filteredProperties.add(st.nextToken());
|
||||
}
|
||||
|
||||
// if supplied, the select takes precedence over the filter (fields/properties) for top-level bean properties
|
||||
if (selectList != null)
|
||||
{
|
||||
for (String select : selectList)
|
||||
{
|
||||
String[] split = select.split("/");
|
||||
filteredProperties.add(split[0]);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Filtering using the following properties: " + filteredProperties);
|
||||
BeanPropertiesFilter filter = new BeanPropertiesFilter(filteredProperties);
|
||||
return filter;
|
||||
}
|
||||
return BeanPropertiesFilter.ALLOW_ALL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the web request and looks for a "relations" parameter Parses the
|
||||
* parameter and produces a list of bean properties to use as a filter A
|
||||
* SimpleBeanPropertiesFilter it returned that uses the properties If no
|
||||
* filter param is set then a default BeanFilter is returned that will never
|
||||
* filter properties (ie. Returns all bean properties).
|
||||
*
|
||||
* @param filterParams String
|
||||
* @return BeanPropertiesFilter - if no parameter then returns a new
|
||||
* ReturnAllBeanProperties class
|
||||
*/
|
||||
public static Map<String, BeanPropertiesFilter> getRelationFilter(String filterParams)
|
||||
{
|
||||
if (filterParams != null)
|
||||
{
|
||||
// Split by a comma when not in a bracket
|
||||
String[] relations = filterParams.split(",(?![^()]*+\\))");
|
||||
Map<String, BeanPropertiesFilter> filterMap = new HashMap<String, BeanPropertiesFilter>(relations.length);
|
||||
|
||||
for (String relation : relations)
|
||||
{
|
||||
int bracketLocation = relation.indexOf("(");
|
||||
if (bracketLocation != -1)
|
||||
{
|
||||
// We have properties
|
||||
String relationKey = relation.substring(0, bracketLocation);
|
||||
String props = relation.substring(bracketLocation + 1, relation.length() - 1);
|
||||
filterMap.put(relationKey, getFilter(props));
|
||||
}
|
||||
else
|
||||
{
|
||||
// no properties so just get the String
|
||||
filterMap.put(relation, getFilter(null));
|
||||
}
|
||||
}
|
||||
return filterMap;
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the "select" parameter and turns it into a List<String> property names
|
||||
* @param selectParam String
|
||||
* @return bean property names potentially using JSON Pointer syntax
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Deprecated
|
||||
public static List<String> getSelectClause(String selectParam) throws InvalidArgumentException
|
||||
{
|
||||
return getClause(selectParam, "SELECT");
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the "include" parameter and turns it into a List<String> property names
|
||||
* @param includeParam String
|
||||
* @return bean property names potentially using JSON Pointer syntax
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<String> getIncludeClause(String includeParam) throws InvalidArgumentException
|
||||
{
|
||||
return getClause(includeParam, "INCLUDE");
|
||||
}
|
||||
|
||||
private static List<String> getClause(String param, String paramName)
|
||||
{
|
||||
if (param == null) return Collections.emptyList();
|
||||
|
||||
try {
|
||||
CommonTree selectedPropsTree = WhereCompiler.compileSelectClause(param);
|
||||
if (selectedPropsTree instanceof CommonErrorNode)
|
||||
{
|
||||
logger.debug("Error parsing the "+paramName+" clause "+selectedPropsTree);
|
||||
throw new InvalidSelectException(paramName, selectedPropsTree);
|
||||
}
|
||||
if (selectedPropsTree.getChildCount() == 0 && !selectedPropsTree.getText().isEmpty())
|
||||
{
|
||||
return Arrays.asList(selectedPropsTree.getText());
|
||||
}
|
||||
List<Tree> children = (List<Tree>) selectedPropsTree.getChildren();
|
||||
if (children!= null && !children.isEmpty())
|
||||
{
|
||||
List<String> properties = new ArrayList<String>(children.size());
|
||||
for (Tree child : children) {
|
||||
properties.add(child.getText());
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
catch (RewriteCardinalityException re)
|
||||
{
|
||||
//Catch any error so it doesn't get thrown up the stack
|
||||
logger.debug("Unhandled Error parsing the "+paramName+" clause: "+re);
|
||||
}
|
||||
catch (RecognitionException e)
|
||||
{
|
||||
logger.debug("Error parsing the \"+paramName+\" clause: "+param);
|
||||
}
|
||||
catch (InvalidQueryException iqe)
|
||||
{
|
||||
throw new InvalidSelectException(paramName, iqe.getQueryParam());
|
||||
}
|
||||
//Default to throw out an invalid query
|
||||
throw new InvalidSelectException(paramName, param);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the "where" parameter and turns it into a Java Object that can be used for querying
|
||||
* @param whereParam String
|
||||
* @return Query a parsed version of the where clause, represented in Java
|
||||
*/
|
||||
public static Query getWhereClause(String whereParam) throws InvalidQueryException
|
||||
{
|
||||
if (whereParam == null) return QueryImpl.EMPTY;
|
||||
|
||||
try {
|
||||
CommonTree whereTree = WhereCompiler.compileWhereClause(whereParam);
|
||||
if (whereTree instanceof CommonErrorNode)
|
||||
{
|
||||
logger.debug("Error parsing the WHERE clause "+whereTree);
|
||||
throw new InvalidQueryException(whereTree);
|
||||
}
|
||||
return new QueryImpl(whereTree);
|
||||
} catch (RewriteCardinalityException re) { //Catch any error so it doesn't get thrown up the stack
|
||||
logger.info("Unhandled Error parsing the WHERE clause: "+re);
|
||||
} catch (RecognitionException e) {
|
||||
whereParam += ", "+WhereCompiler.resolveMessage(e);
|
||||
logger.info("Error parsing the WHERE clause: "+whereParam);
|
||||
}
|
||||
//Default to throw out an invalid query
|
||||
throw new InvalidQueryException(whereParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the Sort parameter as a String and parses it into a List of SortColumn objects.
|
||||
* The format is a comma seperated list of "columnName sortDirection",
|
||||
* e.g. "name DESC, age ASC". It is not case sensitive and the sort direction is optional
|
||||
* It default to sort ASCENDING.
|
||||
* @param sortParams - String passed in on the request
|
||||
* @return - the sort columns or an empty list if the params were invalid.
|
||||
*/
|
||||
public static List<SortColumn> getSort(String sortParams)
|
||||
{
|
||||
if (sortParams != null)
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(sortParams, ",");
|
||||
List<SortColumn> sortedColumns = new ArrayList<SortColumn>(st.countTokens());
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
String token = st.nextToken();
|
||||
StringTokenizer columnDesc = new StringTokenizer(token, " ");
|
||||
if (columnDesc.countTokens() <= 2)
|
||||
{
|
||||
String columnName = columnDesc.nextToken();
|
||||
String sortOrder = SortColumn.ASCENDING;
|
||||
if (columnDesc.hasMoreTokens())
|
||||
{
|
||||
String sortDef = columnDesc.nextToken().toUpperCase();
|
||||
if (SortColumn.ASCENDING.equals(sortDef) || SortColumn.DESCENDING.equals(sortDef))
|
||||
{
|
||||
sortOrder = sortDef;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.debug("Invalid sort order definition ("+sortDef+"). Valid values are "+SortColumn.ASCENDING+" or "+SortColumn.DESCENDING+".");
|
||||
}
|
||||
}
|
||||
sortedColumns.add(new SortColumn(columnName, SortColumn.ASCENDING.equals(sortOrder)));
|
||||
}
|
||||
// filteredProperties.add();
|
||||
}
|
||||
// logger.debug("Filtering using the following properties: " + filteredProperties);
|
||||
// BeanPropertiesFilter filter = new BeanPropertiesFilter(filteredProperties);
|
||||
return sortedColumns;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the body contents from the request
|
||||
*
|
||||
* @param req the request
|
||||
* @param jsonHelper Jackson Helper
|
||||
* @param requiredType the type to return
|
||||
* @return the Object in the required type
|
||||
*/
|
||||
public static <T> T extractJsonContent(WebScriptRequest req, JacksonHelper jsonHelper, Class<T> requiredType)
|
||||
{
|
||||
Reader reader;
|
||||
try
|
||||
{
|
||||
reader = req.getContent().getReader();
|
||||
return jsonHelper.construct(reader, requiredType);
|
||||
}
|
||||
catch (JsonMappingException e)
|
||||
{
|
||||
logger.warn("Could not read content from HTTP request body.", e);
|
||||
throw new InvalidArgumentException("Could not read content from HTTP request body.");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ApiException("Could not read content from HTTP request body.", e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the body contents from the request as a List, the JSON can be an array or just a single value without the [] symbols
|
||||
*
|
||||
* @param req the request
|
||||
* @param jsonHelper Jackson Helper
|
||||
* @param requiredType the type to return (without the List param)
|
||||
* @return A List of "Object" as the required type
|
||||
*/
|
||||
public static <T> List<T> extractJsonContentAsList(WebScriptRequest req, JacksonHelper jsonHelper, Class<T> requiredType)
|
||||
{
|
||||
Reader reader;
|
||||
try
|
||||
{
|
||||
reader = req.getContent().getReader();
|
||||
return jsonHelper.constructList(reader, requiredType);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ApiException("Could not read content from HTTP request body.", e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the id of theObj to the uniqueId. Attempts to find a set method and
|
||||
* invoke it. If it fails it just swallows the exceptions and doesn't throw
|
||||
@@ -471,41 +146,6 @@ public class ResourceWebScriptHelper
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Renders the response result
|
||||
// *
|
||||
// * @param response
|
||||
// * @param result
|
||||
// */
|
||||
// public static void renderResponseDep(Map<String, Object> response, Object result)
|
||||
// {
|
||||
//
|
||||
// if (result == null) { return; }
|
||||
//
|
||||
// if (result instanceof Collection)
|
||||
// {
|
||||
// response.put("list", result);
|
||||
// }
|
||||
// else if (result instanceof CollectionWithPagingInfo)
|
||||
// {
|
||||
// CollectionWithPagingInfo<?> col = (CollectionWithPagingInfo<?>) result;
|
||||
// if (col.getCollection() !=null && !col.getCollection().isEmpty())
|
||||
// {
|
||||
// response.put("list", col);
|
||||
// }
|
||||
// }
|
||||
// else if (result instanceof Pair<?,?>)
|
||||
// {
|
||||
// Pair<?,?> aPair = (Pair<?, ?>) result;
|
||||
// response.put("entry", aPair.getFirst());
|
||||
// response.put("relations", aPair.getSecond());
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// response.put("entry", result);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Looks at the object passed in and recursively expands any @EmbeddedEntityResource annotations or related relationship.
|
||||
* {@link org.alfresco.rest.framework.resource.EmbeddedEntityResource EmbeddedEntityResource} is expanded by calling the ReadById method for this entity.
|
||||
@@ -677,7 +317,7 @@ public class ResourceWebScriptHelper
|
||||
paramFilter = filters.get(resourceKey);
|
||||
}
|
||||
final Params executionParams = Params.valueOf(paramFilter, uniqueEntityId, params.getRequest());
|
||||
final WithResponse callBack = new WithResponse(Status.STATUS_OK, ApiAssistant.DEFAULT_JSON_CONTENT,ApiAssistant.CACHE_NEVER);
|
||||
final WithResponse callBack = new WithResponse(Status.STATUS_OK, ResponseWriter.DEFAULT_JSON_CONTENT,ResponseWriter.CACHE_NEVER);
|
||||
//Read only because this only occurs for GET requests
|
||||
Object result = executor.executeAction(resource, executionParams, callBack);
|
||||
return processAdditionsToTheResponse(null, api, null, executionParams, result);
|
||||
@@ -705,118 +345,6 @@ public class ResourceWebScriptHelper
|
||||
return null; //default
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all request parameters that aren't already known about (eg. not paging or filter params)
|
||||
* and returns them for use.
|
||||
*
|
||||
* @param req - the WebScriptRequest object
|
||||
* @return the request parameters
|
||||
*/
|
||||
public static Map<String, String[]> getRequestParameters(WebScriptRequest req)
|
||||
{
|
||||
if (req!= null)
|
||||
{
|
||||
String[] paramNames = req.getParameterNames();
|
||||
if (paramNames!= null)
|
||||
{
|
||||
Map<String, String[]> requestParameteters = new HashMap<String, String[]>(paramNames.length);
|
||||
|
||||
for (int i = 0; i < paramNames.length; i++)
|
||||
{
|
||||
String paramName = paramNames[i];
|
||||
if (!KNOWN_PARAMS.contains(paramName))
|
||||
{
|
||||
String[] vals = req.getParameterValues(paramName);
|
||||
requestParameteters.put(paramName, vals);
|
||||
}
|
||||
}
|
||||
return requestParameteters;
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the formal set of params that any rest service could potentially have passed in as request params
|
||||
* @param req WebScriptRequest
|
||||
* @return RecognizedParams a POJO containing the params for use with the Params objects
|
||||
*/
|
||||
public static RecognizedParams getRecognizedParams(WebScriptRequest req)
|
||||
{
|
||||
Paging paging = findPaging(req);
|
||||
List<SortColumn> sorting = getSort(req.getParameter(ResourceWebScriptHelper.PARAM_ORDERBY));
|
||||
Map<String, BeanPropertiesFilter> relationFilter = getRelationFilter(req.getParameter(ResourceWebScriptHelper.PARAM_RELATIONS));
|
||||
Query whereQuery = getWhereClause(req.getParameter(ResourceWebScriptHelper.PARAM_WHERE));
|
||||
Map<String, String[]> requestParams = getRequestParameters(req);
|
||||
boolean includeSource = Boolean.valueOf(req.getParameter(ResourceWebScriptHelper.PARAM_INCLUDE_SOURCE_ENTITY));
|
||||
|
||||
List<String> includedFields = getIncludeClause(req.getParameter(ResourceWebScriptHelper.PARAM_INCLUDE));
|
||||
List<String> selectFields = getSelectClause(req.getParameter(ResourceWebScriptHelper.PARAM_SELECT));
|
||||
|
||||
String fields = req.getParameter(ResourceWebScriptHelper.PARAM_FILTER_FIELDS);
|
||||
String properties = req.getParameter(ResourceWebScriptHelper.PARAM_FILTER_PROPERTIES);
|
||||
|
||||
if ((fields != null) && (properties != null))
|
||||
{
|
||||
if (logger.isWarnEnabled())
|
||||
{
|
||||
logger.warn("Taking 'fields' param [" + fields + "] and ignoring deprecated 'properties' param [" + properties + "]");
|
||||
}
|
||||
}
|
||||
|
||||
BeanPropertiesFilter filter = getFilter((fields != null ? fields : properties), includedFields);
|
||||
|
||||
return new RecognizedParams(requestParams, paging, filter, relationFilter, includedFields, selectFields, whereQuery, sorting, includeSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find paging setings based on the request parameters.
|
||||
*
|
||||
* @param req
|
||||
* @return Paging
|
||||
*/
|
||||
public static Paging findPaging(WebScriptRequest req)
|
||||
{
|
||||
int skipped = Paging.DEFAULT_SKIP_COUNT;
|
||||
int max = Paging.DEFAULT_MAX_ITEMS;
|
||||
String skip = req.getParameter(PARAM_PAGING_SKIP);
|
||||
String maxItems = req.getParameter(PARAM_PAGING_MAX);
|
||||
|
||||
try
|
||||
{
|
||||
if (skip != null) { skipped = Integer.parseInt(skip);}
|
||||
if (maxItems != null) { max = Integer.parseInt(maxItems); }
|
||||
if (skipped < 0)
|
||||
{
|
||||
throw new InvalidArgumentException("Negative values not supported for skipCount.");
|
||||
}
|
||||
if (max < 1)
|
||||
{
|
||||
throw new InvalidArgumentException("Only positive values supported for maxItems.");
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException error)
|
||||
{
|
||||
String errorMsg = "Invalid paging parameters skipCount: " + skip + ", maxItems:" + maxItems;
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(errorMsg);
|
||||
}
|
||||
if (skip == null)
|
||||
{
|
||||
errorMsg = "Invalid paging parameter maxItems:" + maxItems;
|
||||
}
|
||||
if (maxItems == null)
|
||||
{
|
||||
errorMsg = "Invalid paging parameter skipCount:" + skip;
|
||||
}
|
||||
throw new InvalidArgumentException(errorMsg);
|
||||
}
|
||||
|
||||
return Paging.valueOf(skipped, max);
|
||||
}
|
||||
|
||||
public void setLocator(ResourceLocator locator)
|
||||
{
|
||||
this.locator = locator;
|
||||
|
@@ -45,6 +45,8 @@ import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResou
|
||||
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
|
||||
import org.alfresco.rest.framework.resource.parameters.Params;
|
||||
import org.alfresco.rest.framework.resource.parameters.Params.RecognizedParams;
|
||||
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
|
||||
import org.alfresco.rest.framework.tools.RequestReader;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||
import org.springframework.extensions.webscripts.WebScriptRequestImpl;
|
||||
@@ -56,7 +58,8 @@ import org.springframework.http.HttpMethod;
|
||||
*
|
||||
* @author Gethin James
|
||||
*/
|
||||
public class ResourceWebScriptPost extends AbstractResourceWebScript implements ParamsExtractor
|
||||
public class ResourceWebScriptPost extends AbstractResourceWebScript implements ParamsExtractor,
|
||||
RecognizedParamsExtractor, RequestReader
|
||||
{
|
||||
|
||||
public ResourceWebScriptPost()
|
||||
@@ -69,7 +72,7 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
|
||||
@Override
|
||||
public Params extractParams(ResourceMetadata resourceMeta, WebScriptRequest req)
|
||||
{
|
||||
final RecognizedParams params = ResourceWebScriptHelper.getRecognizedParams(req);
|
||||
final RecognizedParams params = getRecognizedParams(req);
|
||||
final String entityId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.ENTITY_ID);
|
||||
final String relationshipId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.RELATIONSHIP_ID);
|
||||
final ResourceOperation operation = resourceMeta.getOperation(HttpMethod.POST);
|
||||
@@ -168,7 +171,7 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
|
||||
Object jsonContent = null;
|
||||
if (objType != null)
|
||||
{
|
||||
jsonContent = ResourceWebScriptHelper.extractJsonContent(req, assistant.getJsonHelper(), objType);
|
||||
jsonContent = extractJsonContent(req, assistant.getJsonHelper(), objType);
|
||||
}
|
||||
|
||||
if (isTypeOperation)
|
||||
@@ -203,11 +206,11 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
|
||||
if (isTypeOperation)
|
||||
{
|
||||
// Operations don't support a List as json body
|
||||
return ResourceWebScriptHelper.extractJsonContent(req, assistant.getJsonHelper(), objType);
|
||||
return extractJsonContent(req, assistant.getJsonHelper(), objType);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ResourceWebScriptHelper.extractJsonContentAsList(req, assistant.getJsonHelper(), objType);
|
||||
return extractJsonContentAsList(req, assistant.getJsonHelper(), objType);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -44,6 +44,8 @@ import org.alfresco.rest.framework.resource.content.BasicContentInfo;
|
||||
import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
|
||||
import org.alfresco.rest.framework.resource.parameters.Params;
|
||||
import org.alfresco.rest.framework.resource.parameters.Params.RecognizedParams;
|
||||
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
|
||||
import org.alfresco.rest.framework.tools.RequestReader;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -57,7 +59,8 @@ import org.springframework.http.MediaType;
|
||||
*
|
||||
* @author Gethin James
|
||||
*/
|
||||
public class ResourceWebScriptPut extends AbstractResourceWebScript implements ParamsExtractor
|
||||
public class ResourceWebScriptPut extends AbstractResourceWebScript implements ParamsExtractor,
|
||||
RecognizedParamsExtractor, RequestReader
|
||||
{
|
||||
|
||||
private static Log logger = LogFactory.getLog(ResourceWebScriptPut.class);
|
||||
@@ -75,7 +78,7 @@ public class ResourceWebScriptPut extends AbstractResourceWebScript implements P
|
||||
|
||||
final String relationshipId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.RELATIONSHIP_ID);
|
||||
final String entityId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.ENTITY_ID);
|
||||
final RecognizedParams params = ResourceWebScriptHelper.getRecognizedParams(req);
|
||||
final RecognizedParams params = getRecognizedParams(req);
|
||||
final ResourceOperation operation = resourceMeta.getOperation(HttpMethod.PUT);
|
||||
|
||||
switch (resourceMeta.getType())
|
||||
@@ -87,7 +90,7 @@ public class ResourceWebScriptPut extends AbstractResourceWebScript implements P
|
||||
} else
|
||||
{
|
||||
|
||||
Object putEnt = ResourceWebScriptHelper.extractJsonContent(req, assistant.getJsonHelper(), resourceMeta.getObjectType(operation));
|
||||
Object putEnt = extractJsonContent(req, assistant.getJsonHelper(), resourceMeta.getObjectType(operation));
|
||||
return Params.valueOf(entityId,params,putEnt, req);
|
||||
}
|
||||
case RELATIONSHIP:
|
||||
@@ -96,7 +99,7 @@ public class ResourceWebScriptPut extends AbstractResourceWebScript implements P
|
||||
throw new UnsupportedResourceOperationException("PUT is executed against the instance URL");
|
||||
} else
|
||||
{
|
||||
Object putRel = ResourceWebScriptHelper.extractJsonContent(req, assistant.getJsonHelper(), resourceMeta.getObjectType(operation));
|
||||
Object putRel = extractJsonContent(req, assistant.getJsonHelper(), resourceMeta.getObjectType(operation));
|
||||
ResourceWebScriptHelper.setUniqueId(putRel,relationshipId);
|
||||
return Params.valueOf(entityId, params, putRel, req);
|
||||
}
|
||||
|
Reference in New Issue
Block a user