001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     <a href="mailto:grenard@nuxeo.com">Guillaume Renard</a>
016 *
017 */
018
019package org.nuxeo.ecm.restapi.server.jaxrs.routing.io;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.Serializable;
024import java.lang.annotation.Annotation;
025import java.lang.reflect.Type;
026import java.util.Map;
027import javax.servlet.http.HttpServletRequest;
028import javax.ws.rs.WebApplicationException;
029import javax.ws.rs.core.Context;
030import javax.ws.rs.core.MediaType;
031import javax.ws.rs.core.MultivaluedMap;
032import javax.ws.rs.core.Response;
033import javax.ws.rs.ext.MessageBodyReader;
034import javax.ws.rs.ext.Provider;
035
036import org.apache.commons.io.IOUtils;
037import org.apache.commons.logging.Log;
038import org.apache.commons.logging.LogFactory;
039import org.codehaus.jackson.JsonFactory;
040import org.codehaus.jackson.JsonNode;
041import org.codehaus.jackson.JsonParseException;
042import org.codehaus.jackson.JsonParser;
043import org.codehaus.jackson.JsonToken;
044import org.nuxeo.ecm.core.api.CoreSession;
045import org.nuxeo.ecm.core.api.IdRef;
046import org.nuxeo.ecm.core.api.NuxeoException;
047import org.nuxeo.ecm.platform.routing.api.DocumentRoutingConstants;
048import org.nuxeo.ecm.platform.task.Task;
049import org.nuxeo.ecm.restapi.server.jaxrs.routing.io.util.JsonEncodeDecodeUtils;
050import org.nuxeo.ecm.restapi.server.jaxrs.routing.model.TaskCompletionRequest;
051import org.nuxeo.ecm.webengine.WebException;
052import org.nuxeo.ecm.webengine.jaxrs.session.SessionFactory;
053
054/**
055 * @since 7.2
056 */
057@Provider
058public class TaskCompletionRequestReader implements MessageBodyReader<TaskCompletionRequest> {
059
060    protected static final Log log = LogFactory.getLog(TaskCompletionRequestReader.class);
061
062    @Context
063    private JsonFactory factory;
064
065    @Context
066    HttpServletRequest request;
067
068    @Override
069    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
070        return TaskCompletionRequest.class.isAssignableFrom(type);
071    }
072
073    @Override
074    public TaskCompletionRequest readFrom(Class<TaskCompletionRequest> type, Type genericType,
075            Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
076            InputStream entityStream) throws IOException, WebApplicationException {
077        String content = IOUtils.toString(entityStream);
078        if (content.isEmpty()) {
079            if (content.isEmpty()) {
080                throw new WebException("No content in request body", Response.Status.BAD_REQUEST.getStatusCode());
081            }
082
083        }
084
085        try {
086            return readRequest(content, httpHeaders);
087        } catch (IOException | NuxeoException | ClassNotFoundException e) {
088            throw WebException.wrap(e);
089        }
090    }
091
092    private TaskCompletionRequest readRequest(String content, MultivaluedMap<String, String> httpHeaders)
093            throws IOException, ClassNotFoundException {
094        JsonParser jp = factory.createJsonParser(content);
095        return readJson(jp, httpHeaders);
096    }
097
098    private TaskCompletionRequest readJson(JsonParser jp, MultivaluedMap<String, String> httpHeaders)
099            throws JsonParseException, IOException, ClassNotFoundException {
100        CoreSession session = SessionFactory.getSession(request);
101        JsonToken tok = jp.nextToken();
102
103        // skip {
104        if (jp.getCurrentToken() == JsonToken.START_OBJECT) {
105            tok = jp.nextToken();
106        }
107        String id = null;
108        String comment = null;
109        JsonNode variableNode = null;
110        Map<String, Serializable> variables = null;
111        while (tok != JsonToken.END_OBJECT) {
112            String key = jp.getCurrentName();
113            jp.nextToken();
114            if ("id".equals(key)) {
115                id = jp.readValueAs(String.class);
116            } else if ("comment".equals(key)) {
117                comment = jp.readValueAs(String.class);
118            } else if ("variables".equals(key)) {
119                variableNode = jp.readValueAsTree();
120            } else if ("entity-type".equals(key)) {
121                String entityType = jp.readValueAs(String.class);
122                if (!TaskWriter.ENTITY_TYPE.equals(entityType)) {
123                    throw new WebApplicationException(Response.Status.BAD_REQUEST);
124                }
125            } else {
126                log.debug("Unknown key: " + key);
127                jp.skipChildren();
128            }
129            tok = jp.nextToken();
130
131        }
132
133        if (id == null) {
134            throw new WebException("No id found in request body", Response.Status.BAD_REQUEST.getStatusCode());
135        }
136
137        TaskCompletionRequest result = new TaskCompletionRequest();
138        Task originalTask = session.getDocument(new IdRef(id)).getAdapter(Task.class);
139        final String nodeId = originalTask.getVariable(DocumentRoutingConstants.TASK_NODE_ID_KEY);
140        String workflowInstanceId = originalTask.getProcessId();
141        NodeAccessRunner nodeAccessRunner = new NodeAccessRunner(session, workflowInstanceId, nodeId);
142        nodeAccessRunner.runUnrestricted();
143        if (variableNode != null) {
144            variables = JsonEncodeDecodeUtils.decodeVariables(variableNode, nodeAccessRunner.node.getVariables(), session);
145        }
146        result.setVariables(variables);
147        result.setComment(comment);
148
149        return result;
150    }
151
152}