/* * R Service Bus * * Copyright (c) Copyright of Open Analytics NV, 2010-2015 * * =========================================================================== * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package eu.openanalytics.rsb.component; import java.io.IOException; import java.util.GregorianCalendar; import java.util.UUID; import javax.annotation.Resource; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import org.springframework.stereotype.Component; import eu.openanalytics.rsb.Constants; import eu.openanalytics.rsb.Util; import eu.openanalytics.rsb.message.AbstractFunctionCallJob; import eu.openanalytics.rsb.message.AbstractResult; import eu.openanalytics.rsb.message.AbstractWorkItem.Source; import eu.openanalytics.rsb.message.JsonFunctionCallJob; import eu.openanalytics.rsb.message.XmlFunctionCallJob; /** * Processes synchronous R job requests. Currently only supports XML and JSON function calls.<br/> * <b>This processor bypasses the messaging infrastructure so it is not a fair player compared to * the other REST/SOAP entry points, thus it should be used with care!</b> * * @author "OpenAnalytics <rsb.development@openanalytics.eu>" */ @Component("processResource") @Path("/" + Constants.PROCESS_PATH) public class ProcessResource extends AbstractResource { private interface FunctionCallJobBuilder { AbstractFunctionCallJob build(final String applicationName, final UUID jobId, final GregorianCalendar submissionTime) throws IOException; } @Resource private JobProcessor jobProcessor; // exposed for unit testing public void setJobProcessor(final JobProcessor jobProcessor) { this.jobProcessor = jobProcessor; } /** * Handles a function call job with a JSON payload. * * @param jsonArgument Argument passed to the function called on RServi. * @param httpHeaders * @return * @throws Exception */ @POST @Consumes(Constants.JSON_CONTENT_TYPE) @Produces(Constants.JSON_CONTENT_TYPE) public Response processJsonFunctionCallJob(final String jsonArgument, @Context final HttpHeaders httpHeaders) throws Exception { return handleNewRestJob(httpHeaders, new FunctionCallJobBuilder() { public AbstractFunctionCallJob build(final String applicationName, final UUID jobId, final GregorianCalendar submissionTime) { return new JsonFunctionCallJob(Source.REST_IMMEDIATE, applicationName, getUserName(), jobId, submissionTime, jsonArgument); } }); } /** * Handles a function call job with a XML payload. * * @param xmlArgument Argument passed to the function called on RServi. * @param httpHeaders * @return * @throws Exception */ @POST @Consumes(Constants.XML_CONTENT_TYPE) @Produces(Constants.XML_CONTENT_TYPE) public Response processXmlFunctionCallJob(final String xmlArgument, @Context final HttpHeaders httpHeaders) throws Exception { return handleNewRestJob(httpHeaders, new FunctionCallJobBuilder() { public AbstractFunctionCallJob build(final String applicationName, final UUID jobId, final GregorianCalendar submissionTime) { return new XmlFunctionCallJob(Source.REST_IMMEDIATE, applicationName, getUserName(), jobId, submissionTime, xmlArgument); } }); } private Response handleNewRestJob(final HttpHeaders httpHeaders, final FunctionCallJobBuilder jobBuilder) throws Exception { final String applicationName = Util.getSingleHeader(httpHeaders, Constants.APPLICATION_NAME_HTTP_HEADER); return handleNewJob(applicationName, jobBuilder); } private Response handleNewJob(final String applicationName, final FunctionCallJobBuilder jobBuilder) throws Exception { final UUID jobId = UUID.randomUUID(); final AbstractFunctionCallJob job = jobBuilder.build(applicationName, jobId, (GregorianCalendar) GregorianCalendar.getInstance()); final AbstractResult<?> result = jobProcessor.processDirect(job); if (result.isSuccess()) { return Response.ok(result.getPayload()).build(); } else { return Response.status(Status.BAD_REQUEST).entity(result.getPayload()).build(); } } }