From 8b7c8003959e7c35bde2e418bee2de5e07eacb41 Mon Sep 17 00:00:00 2001 From: Marco Rietveld Date: Thu, 3 Oct 2013 16:36:23 +0200 Subject: [PATCH] JBPM-3814 BZ 1002724 Fixes that use SeamTransaction injection (cherry picked from commit ca439c9dd697bcd2418efaf32d3ed597f8246395) --- .../remote/cdi/ProcessRequestBean.java | 77 ----- .../remote/jms/RequestMessageBean.java | 82 +++++- .../services/remote/rest/ResourceBase.java | 111 ++++++- .../remote/rest/RestProcessRequestBean.java | 274 ++++++++++++++++++ .../services/remote/rest/RuntimeResource.java | 233 ++++++++++----- .../services/remote/rest/TaskResource.java | 104 ++++--- .../DescriptiveExceptionHandler.java | 6 + .../remote/util/CommandsRequestUtil.java | 70 ----- 8 files changed, 665 insertions(+), 292 deletions(-) delete mode 100644 kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/cdi/ProcessRequestBean.java create mode 100644 kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/RestProcessRequestBean.java delete mode 100644 kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/util/CommandsRequestUtil.java diff --git a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/cdi/ProcessRequestBean.java b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/cdi/ProcessRequestBean.java deleted file mode 100644 index 28541f9084..0000000000 --- a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/cdi/ProcessRequestBean.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.kie.services.remote.cdi; - - -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; - -import org.jboss.resteasy.spi.UnauthorizedException; -import org.jbpm.services.task.exception.PermissionDeniedException; -import org.kie.api.command.Command; -import org.kie.api.runtime.KieSession; -import org.kie.api.runtime.manager.Context; -import org.kie.api.runtime.manager.RuntimeEngine; -import org.kie.api.runtime.manager.RuntimeManager; -import org.kie.api.task.TaskService; -import org.kie.internal.runtime.manager.context.EmptyContext; -import org.kie.internal.runtime.manager.context.ProcessInstanceIdContext; -import org.kie.internal.task.api.InternalTaskService; -import org.kie.services.client.serialization.jaxb.impl.JaxbExceptionResponse; -import org.kie.services.remote.exception.DomainNotFoundBadRequestException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@ApplicationScoped -public class ProcessRequestBean { - - private static final Logger logger = LoggerFactory.getLogger(ProcessRequestBean.class); - - @Inject - private RuntimeManagerManager runtimeMgrMgr; - - @Inject - private TaskService taskService; - - public Object doKieSessionOperation(Command cmd, String deploymentId, Long processInstanceId) { - Object result = null; - try { - KieSession kieSession = getRuntimeEngine(deploymentId, processInstanceId).getKieSession(); - result = kieSession.execute(cmd); - } catch( Exception e ) { - JaxbExceptionResponse exceptResp = new JaxbExceptionResponse(e, cmd); - logger.warn( "Unable to execute " + exceptResp.getCommandName() + " because of " + e.getClass().getSimpleName() + ": " + e.getMessage()); - logger.trace("Stack trace: \n", e); - result = exceptResp; - } - return result; - } - - public Object doTaskOperation(Command cmd) { - Object result = null; - try { - result = ((InternalTaskService) taskService).execute(cmd); - } catch( PermissionDeniedException pde ) { - throw new UnauthorizedException(pde.getMessage(), pde); - } catch( Exception e ) { - JaxbExceptionResponse exceptResp = new JaxbExceptionResponse(e, cmd); - logger.warn( "Unable to execute " + exceptResp.getCommandName() + " because of " + e.getClass().getSimpleName() + ": " + e.getMessage()); - logger.trace("Stack trace: \n", e); - result = exceptResp; - } - return result; - } - - protected RuntimeEngine getRuntimeEngine(String domainName, Long processInstanceId) { - RuntimeManager runtimeManager = runtimeMgrMgr.getRuntimeManager(domainName); - Context runtimeContext; - if (processInstanceId != null) { - runtimeContext = new ProcessInstanceIdContext(processInstanceId); - } else { - runtimeContext = EmptyContext.get(); - } - if( runtimeManager == null ) { - throw new DomainNotFoundBadRequestException("No runtime manager could be found for domain '" + domainName + "'."); - } - return runtimeManager.getRuntimeEngine(runtimeContext); - } - -} diff --git a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/jms/RequestMessageBean.java b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/jms/RequestMessageBean.java index 864c7c7a4d..9ecf2ff48d 100644 --- a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/jms/RequestMessageBean.java +++ b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/jms/RequestMessageBean.java @@ -24,19 +24,29 @@ import org.jbpm.services.task.commands.TaskCommand; import org.jbpm.services.task.exception.PermissionDeniedException; import org.kie.api.command.Command; +import org.kie.api.runtime.KieSession; +import org.kie.api.runtime.manager.Context; +import org.kie.api.runtime.manager.RuntimeEngine; +import org.kie.api.runtime.manager.RuntimeManager; +import org.kie.api.task.TaskService; +import org.kie.internal.runtime.manager.context.EmptyContext; +import org.kie.internal.runtime.manager.context.ProcessInstanceIdContext; +import org.kie.internal.task.api.InternalTaskService; import org.kie.services.client.api.command.AcceptedCommands; import org.kie.services.client.serialization.jaxb.JaxbSerializationProvider; import org.kie.services.client.serialization.jaxb.impl.JaxbCommandsRequest; import org.kie.services.client.serialization.jaxb.impl.JaxbCommandsResponse; -import org.kie.services.remote.cdi.ProcessRequestBean; +import org.kie.services.client.serialization.jaxb.impl.JaxbExceptionResponse; +import org.kie.services.remote.cdi.RuntimeManagerManager; import org.kie.services.remote.exception.DomainNotFoundBadRequestException; import org.kie.services.remote.exception.KieRemoteServicesInternalError; +import org.kie.services.remote.rest.RestProcessRequestBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class is the link between incoming request (whether via REST or JMS or .. whatever) - * and the bean that processes the requests, the {@link ProcessRequestBean}. + * and the bean that processes the requests, the {@link RestProcessRequestBean}. *

* Responses to requests are not placed on the reply-to queue, but on the answer queue. *

Because there are multiple queues to which an instance of this class could listen to, the (JMS queue) configuration is @@ -49,12 +59,15 @@ public class RequestMessageBean implements MessageListener { @Resource(mappedName = "java:/ConnectionFactory") private ConnectionFactory connectionFactory; - @Inject - private ProcessRequestBean processRequestBean; - private String RESPONSE_QUEUE_NAME = null; private static String RESPONSE_QUEUE_NAME_PROPERTY = "kie.services.jms.queues.response"; + @Inject + private RuntimeManagerManager runtimeMgrMgr; + + @Inject + private TaskService taskService; + @PostConstruct public void init() { RESPONSE_QUEUE_NAME = System.getProperty(RESPONSE_QUEUE_NAME_PROPERTY, "queue/KIE.RESPONSE.ALL"); @@ -81,7 +94,7 @@ public void onMessage(Message message) { // 2. process request JaxbCommandsResponse jaxbResponse; if (cmdsRequest != null) { - jaxbResponse = processJaxbCommandsRequest(cmdsRequest, processRequestBean); + jaxbResponse = processJaxbCommandsRequest(cmdsRequest); } else { // Failure reasons have been logged in deserializeRequest(). logger.error("Stopping processing of request message due to errors: see above."); @@ -190,7 +203,7 @@ private Message serializeResponse(Session session, String msgId, int serializati return byteMsg; } - public JaxbCommandsResponse processJaxbCommandsRequest(JaxbCommandsRequest request, ProcessRequestBean requestBean) { + public JaxbCommandsResponse processJaxbCommandsRequest(JaxbCommandsRequest request) { // If exceptions are happening here, then there is something REALLY wrong and they should be thrown. JaxbCommandsResponse jaxbResponse = new JaxbCommandsResponse(request); List> commands = request.getCommands(); @@ -207,9 +220,9 @@ public JaxbCommandsResponse processJaxbCommandsRequest(JaxbCommandsRequest reque Object cmdResult = null; if (cmd instanceof TaskCommand) { - cmdResult = internalDoTaskOperation(requestBean, cmd, jaxbResponse, i); + cmdResult = internalDoTaskOperation(cmd, jaxbResponse, i); } else { - cmdResult = internalDoKieSessionOperation(requestBean, cmd, request, jaxbResponse, i); + cmdResult = internalDoKieSessionOperation( cmd, request, jaxbResponse, i); } if (cmdResult != null) { try { @@ -232,10 +245,10 @@ public JaxbCommandsResponse processJaxbCommandsRequest(JaxbCommandsRequest reque } @TransactionAttribute(TransactionAttributeType.REQUIRED) - public Object internalDoTaskOperation(ProcessRequestBean requestBean, Command cmd, JaxbCommandsResponse jaxbResponse, int i) { + public Object internalDoTaskOperation(Command cmd, JaxbCommandsResponse jaxbResponse, int i) { Object cmdResult; try { - cmdResult = requestBean.doTaskOperation(cmd); + cmdResult = doTaskOperation(cmd); } catch( UnauthorizedException ue ) { Throwable cause = ue.getCause(); if( cause instanceof PermissionDeniedException ) { @@ -250,10 +263,10 @@ public Object internalDoTaskOperation(ProcessRequestBean requestBean, Command } @TransactionAttribute(TransactionAttributeType.NEVER) - public Object internalDoKieSessionOperation(ProcessRequestBean requestBean, Command cmd, JaxbCommandsRequest request, JaxbCommandsResponse jaxbResponse, int i) { + public Object internalDoKieSessionOperation(Command cmd, JaxbCommandsRequest request, JaxbCommandsResponse jaxbResponse, int i) { Object cmdResult; try { - cmdResult = requestBean.doKieSessionOperation(cmd, request.getDeploymentId(), request.getProcessInstanceId()); + cmdResult = doKieSessionOperation(cmd, request.getDeploymentId(), request.getProcessInstanceId()); } catch( DomainNotFoundBadRequestException dnfbre ) { logger.warn( dnfbre.getMessage() ); jaxbResponse.addException(dnfbre, i, cmd); @@ -261,4 +274,47 @@ public Object internalDoKieSessionOperation(ProcessRequestBean requestBean, Comm } return cmdResult; } + + private Object doKieSessionOperation(Command cmd, String deploymentId, Long processInstanceId) { + Object result = null; + try { + KieSession kieSession = getRuntimeEngine(deploymentId, processInstanceId).getKieSession(); + result = kieSession.execute(cmd); + } catch( Exception e ) { + JaxbExceptionResponse exceptResp = new JaxbExceptionResponse(e, cmd); + logger.warn( "Unable to execute " + exceptResp.getCommandName() + " because of " + e.getClass().getSimpleName() + ": " + e.getMessage()); + logger.trace("Stack trace: \n", e); + result = exceptResp; + } + return result; + } + + private Object doTaskOperation(Command cmd) { + Object result = null; + try { + result = ((InternalTaskService) taskService).execute(cmd); + } catch( PermissionDeniedException pde ) { + throw new UnauthorizedException(pde.getMessage(), pde); + } catch( Exception e ) { + JaxbExceptionResponse exceptResp = new JaxbExceptionResponse(e, cmd); + logger.warn( "Unable to execute " + exceptResp.getCommandName() + " because of " + e.getClass().getSimpleName() + ": " + e.getMessage()); + logger.trace("Stack trace: \n", e); + result = exceptResp; + } + return result; + } + + protected RuntimeEngine getRuntimeEngine(String domainName, Long processInstanceId) { + RuntimeManager runtimeManager = runtimeMgrMgr.getRuntimeManager(domainName); + Context runtimeContext; + if (processInstanceId != null) { + runtimeContext = new ProcessInstanceIdContext(processInstanceId); + } else { + runtimeContext = EmptyContext.get(); + } + if( runtimeManager == null ) { + throw new DomainNotFoundBadRequestException("No runtime manager could be found for domain '" + domainName + "'."); + } + return runtimeManager.getRuntimeEngine(runtimeContext); + } } diff --git a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/ResourceBase.java b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/ResourceBase.java index 3d3622531e..430f6f42fe 100644 --- a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/ResourceBase.java +++ b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/ResourceBase.java @@ -14,22 +14,114 @@ import javax.ws.rs.core.Variant; import org.jboss.resteasy.spi.BadRequestException; +import org.jboss.resteasy.spi.NotAcceptableException; +import org.jboss.resteasy.spi.NotFoundException; +import org.jbpm.services.task.commands.CompleteTaskCommand; +import org.jbpm.services.task.commands.ExitTaskCommand; +import org.jbpm.services.task.commands.FailTaskCommand; +import org.jbpm.services.task.commands.GetTaskCommand; +import org.jbpm.services.task.commands.SkipTaskCommand; +import org.jbpm.services.task.commands.TaskCommand; import org.jbpm.services.task.impl.model.GroupImpl; import org.jbpm.services.task.impl.model.TaskImpl; import org.jbpm.services.task.impl.model.UserImpl; import org.jbpm.services.task.query.TaskSummaryImpl; +import org.kie.api.command.Command; import org.kie.api.task.model.OrganizationalEntity; import org.kie.api.task.model.Status; +import org.kie.api.task.model.Task; +import org.kie.services.client.api.command.AcceptedCommands; +import org.kie.services.client.serialization.jaxb.impl.JaxbCommandsRequest; +import org.kie.services.client.serialization.jaxb.impl.JaxbCommandsResponse; +import org.kie.services.client.serialization.jaxb.impl.JaxbExceptionResponse; +import org.kie.services.remote.cdi.RuntimeManagerManager; +import org.kie.services.remote.exception.KieRemoteServicesInternalError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class ResourceBase { - private static List variants + private static final Logger logger = LoggerFactory.getLogger(ResourceBase.class); + + // Seam-Transaction ---------------------------------------------------------------------------------------------------------- + + public static JaxbCommandsResponse restProcessJaxbCommandsRequest(JaxbCommandsRequest request, RestProcessRequestBean requestBean) { + // If exceptions are happening here, then there is something REALLY wrong and they should be thrown. + JaxbCommandsResponse jaxbResponse = new JaxbCommandsResponse(request); + List> commands = request.getCommands(); + + if (commands != null) { + int cmdListSize = commands.size(); + for (int i = 0; i < cmdListSize; ++i) { + boolean restartTx = !( i == cmdListSize - 1 ); // restart tx for all cmds except the last one + Command cmd = commands.get(i); + if (!AcceptedCommands.getSet().contains(cmd.getClass())) { + throw new NotAcceptableException("The execute REST operation does not accept " + cmd.getClass().getName() + " instances."); + } + logger.debug("Processing command " + cmd.getClass().getSimpleName()); + Object cmdResult = null; + try { + if (cmd instanceof TaskCommand) { + String errorMsg = "Unable to execute command " + cmd.getClass().getSimpleName(); + TaskCommand taskCmd = (TaskCommand) cmd; + if( cmd instanceof CompleteTaskCommand + || cmd instanceof ExitTaskCommand + || cmd instanceof FailTaskCommand + || cmd instanceof SkipTaskCommand ) { + cmdResult = requestBean.doTaskOperationOnDeployment( + taskCmd, + errorMsg, + request.getDeploymentId(), + restartTx); // restart commit + } else { + cmdResult = requestBean.doTaskOperation(taskCmd, errorMsg); + } + } else { + cmdResult = requestBean.doKieSessionOperation( + cmd, + request.getDeploymentId(), + request.getProcessInstanceId(), + "Unable to execute command " + cmd.getClass().getSimpleName(), + true, // commit + restartTx); // restart commit + } + } catch(Exception e) { + jaxbResponse.addException(e, i, cmd); + logger.warn("Unable to execute " + cmd.getClass().getSimpleName() + + " because of " + e.getClass().getSimpleName() + ": " + e.getMessage()); + logger.trace("Stack trace: \n", e); + } + if (cmdResult != null) { + try { + // addResult could possibly throw an exception, which is why it's here and not above + jaxbResponse.addResult(cmdResult, i, cmd); + } catch (Exception e) { + logger.error("Unable to add result from " + cmd.getClass().getSimpleName() + "/" + i + " because of " + + e.getClass().getSimpleName(), e); + logger.trace("Stack trace: \n", e); + jaxbResponse.addException(e, i, cmd); + } + } + } + } + + if (commands == null || commands.isEmpty()) { + logger.info("Commands request object with no commands sent!"); + } + + return jaxbResponse; + } + + // JSON / JAXB --------------------------------------------------------------------------------------------------------------- + + public static List variants = Variant.mediaTypes(MediaType.APPLICATION_XML_TYPE, MediaType.APPLICATION_JSON_TYPE).build(); - protected Variant getVariant(Request restRequest) { + protected static Variant getVariant(Request restRequest) { return restRequest.selectVariant(variants); } - protected Response createCorrectVariant(Object responseObj, Request restRequest) { + protected static Response createCorrectVariant(Object responseObj, Request restRequest) { Variant v = getVariant(restRequest); if( v != null ) { return Response.ok(responseObj, v).build(); @@ -37,16 +129,9 @@ protected Response createCorrectVariant(Object responseObj, Request restRequest) return Response.notAcceptable(variants).build(); } } - - protected static String checkThatOperationExists(String operation, String[] possibleOperations) { - for (String oper : possibleOperations) { - if (oper.equals(operation.trim().toLowerCase())) { - return oper; - } - } - throw new BadRequestException("Operation '" + operation + "' is not supported on tasks."); - } + // Request Params ------------------------------------------------------------------------------------------------------------- + protected static Map> getRequestParams(HttpServletRequest request) { Map> parameters = new HashMap>(); Enumeration names = request.getParameterNames(); @@ -231,6 +316,8 @@ protected static List convertStringListToStatusList( List status return statusList; } + // Pagination ---------------------------------------------------------------------------------------------------------------- + protected static int [] getPageNumAndPageSize(Map> params) { int [] pageInfo = new int[3]; Number page = getNumberParam("page", false, params, "query", false); diff --git a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/RestProcessRequestBean.java b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/RestProcessRequestBean.java new file mode 100644 index 0000000000..3c2d12eb99 --- /dev/null +++ b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/RestProcessRequestBean.java @@ -0,0 +1,274 @@ +package org.kie.services.remote.rest; + +import javax.enterprise.context.RequestScoped; +import javax.enterprise.event.Event; +import javax.inject.Inject; +import javax.persistence.EntityManager; +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.NotSupportedException; +import javax.transaction.RollbackException; +import javax.transaction.Status; +import javax.transaction.SystemException; + +import org.jboss.resteasy.spi.InternalServerErrorException; +import org.jboss.resteasy.spi.UnauthorizedException; +import org.jboss.seam.transaction.DefaultTransaction; +import org.jboss.seam.transaction.SeamTransaction; +import org.jboss.solder.exception.control.ExceptionToCatch; +import org.jbpm.services.task.commands.CompleteTaskCommand; +import org.jbpm.services.task.commands.FailTaskCommand; +import org.jbpm.services.task.commands.GetTaskCommand; +import org.jbpm.services.task.commands.TaskCommand; +import org.jbpm.services.task.exception.PermissionDeniedException; +import org.kie.api.command.Command; +import org.kie.api.runtime.KieSession; +import org.kie.api.runtime.manager.Context; +import org.kie.api.runtime.manager.RuntimeEngine; +import org.kie.api.runtime.manager.RuntimeManager; +import org.kie.api.task.TaskService; +import org.kie.api.task.model.Task; +import org.kie.internal.runtime.manager.context.EmptyContext; +import org.kie.internal.runtime.manager.context.ProcessInstanceIdContext; +import org.kie.internal.task.api.InternalTaskService; +import org.kie.services.client.serialization.jaxb.impl.JaxbExceptionResponse; +import org.kie.services.remote.cdi.RuntimeManagerManager; +import org.kie.services.remote.exception.DomainNotFoundBadRequestException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is used by both the {@link RuntimeResource} and {@link TaskResource} to do the core operations on + * the Deployment/Runtime's {@link KieSession} and {@link TaskService}. + *

+ * It contains the necessary logic to do the following: + *
    + *
  • Retrieve the KieSession or TaskService
  • + *
  • Execute the submitted command
  • + *
  • Call commit on the given {@link SeamTransaction} instance
  • + *
+ * The commit on the {@link SeamTransaction} is necessary in order to avoid race-conditions + * involving the application scoped {@link EntityManager} used in the {@link KieSession}. + */ +@RequestScoped +public class RestProcessRequestBean { + + private static final Logger logger = LoggerFactory.getLogger(RestProcessRequestBean.class); + + /* KIE processing */ + @Inject + private RuntimeManagerManager runtimeMgrMgr; + + @Inject + private TaskService taskService; + + /* Transaction control */ + @Inject + @DefaultTransaction + private SeamTransaction tx; + + @Inject + Event txExceptionEvent; + + /** + * Executes a command on the {@link KieSession} from the proper {@link RuntimeManager}. This method + * ends up synchronizing around the retrieved {@link KieSession} in order to avoid race-conditions. + * + * @param cmd The command to be executed. + * @param deploymentId The id of the runtime. + * @param processInstanceId The process instance id, if available. + * @param commit Whether or not to commit (the {@link SeamTransaction}) at after the {@link Command} has been completed. + * @return The result of the {@link Command}. + */ + public Object doKieSessionOperation(Command cmd, String deploymentId, Long processInstanceId, String errorMsg, + boolean commit, boolean restartTx) { + Object result = null; + try { + RuntimeEngine runtimeEngine = getRuntimeEngine(deploymentId, processInstanceId); + synchronized (runtimeEngine) { + KieSession kieSession = runtimeEngine.getKieSession(); + try { + result = kieSession.execute(cmd); + } finally { + if (commit) { + commit(tx, txExceptionEvent, logger); + } + if(restartTx) { + start(tx, txExceptionEvent, logger); + } + } + } + } catch (Exception e) { + if( e instanceof RuntimeException ) { + throw (RuntimeException) e; + } else { + throw new InternalServerErrorException(errorMsg, e); + } + } + return result; + } + + /** + * A variant of the above method which always calls commit once the {@link Command} has completed. + * + * @param cmd The command to be executed. + * @param deploymentId The id of the runtime. + * @param processInstanceId The process id, if available. + * @return The result of the executed command. + */ + public Object doKieSessionOperation(Command cmd, String deploymentId, Long processInstanceId, String errorMsg) { + return doKieSessionOperation(cmd, deploymentId, processInstanceId, errorMsg, true); + } + + /** + * A variant of the above method which commits but does not restart the {@link SeamTransaction} once the command has completed. + * + * @param cmd The command to be executed. + * @param deploymentId The id of the runtime. + * @param processInstanceId The process id, if available. + * @return The result of the executed command. + */ + public Object doKieSessionOperation(Command cmd, String deploymentId, Long processInstanceId, String errorMsg, boolean commit) { + return doKieSessionOperation(cmd, deploymentId, processInstanceId, errorMsg, commit, false); + } + + /** + * Executes a command on the injected {@link TaskService} instance. + *

+ * Should be used only for commands that end up affecting the associated {@link KieSession}. + * Since the {@link CompleteTaskCommand} ends up signalling the {@link KieSession}, we make sure to + * first retrieve the proper {@link KieSession} in order to synchronize around it. + * + * @param cmd The command to be executed. + * @param errorMsg The message to be added to any (non-runtime) exceptions thrown. + * @param deploymentId The deployment id of the runtime. + * @return The result of the completed command. + */ + public Object doTaskOperationOnDeployment(TaskCommand cmd, String errorMsg, String deploymentId, boolean restartTx) { + Object result = null; + try { + if( deploymentId != null ) { + RuntimeEngine runtimeEngine = getRuntimeEngine(deploymentId, null); + synchronized (runtimeEngine) { + try { + ((InternalTaskService) taskService).execute(cmd); + } finally { + commit(tx, txExceptionEvent, logger); + if( restartTx ) { + start(tx, txExceptionEvent, logger); + } + } + } + } else { + result = ((InternalTaskService) taskService).execute(cmd); + } + } catch (PermissionDeniedException pde) { + throw new UnauthorizedException(pde.getMessage(), pde); + } catch (RuntimeException re) { + throw re; + } catch( Exception e ) { + throw new InternalServerErrorException(errorMsg, e); + } + return result; + } + + /** + * Variant of the above method which does not restart the transaction. + * @param cmd The {@link Command} to be executed. + * @param errorMsg The error message for any exception thrown. + * @param deploymentId The deployment id. + * @return The result of the given {@link Command}. + */ + public Object doTaskOperationOnDeployment(TaskCommand cmd, String errorMsg, String deploymentId) { + return doTaskOperationOnDeployment(cmd, errorMsg, deploymentId, false); + } + + /** + * Executes a command on the {@link TaskService} (without synchronizing around the {@link KieSession}) + * @param cmd The command to be executed. + * @param errorMsg The error message to be attached to any exceptions thrown. + * @return The result of the completed command. + */ + public Object doTaskOperation(TaskCommand cmd, String errorMsg) { + return doTaskOperationOnDeployment(cmd, errorMsg, null); + } + + /** + * Retrieve the relevant {@link RuntimeEngine} instance. + * + * @param deploymentId The id of the deployment for the {@link RuntimeEngine}. + * @param processInstanceId The process instance id, if available. + * @return The {@link RuntimeEngine} instance. + */ + private RuntimeEngine getRuntimeEngine(String deploymentId, Long processInstanceId) { + RuntimeManager runtimeManager = runtimeMgrMgr.getRuntimeManager(deploymentId); + Context runtimeContext; + if (processInstanceId != null) { + runtimeContext = new ProcessInstanceIdContext(processInstanceId); + } else { + runtimeContext = EmptyContext.get(); + } + if (runtimeManager == null) { + throw new DomainNotFoundBadRequestException("No runtime manager could be found for deployment '" + deploymentId + "'."); + } + return runtimeManager.getRuntimeEngine(runtimeContext); + } + + /** + * Commit the given {@link SeamTransaction}. + * + * @param tx The {@link SeamTransaction} instance. + * @param txExceptionEvent The CDI Event used in order to communicate with the seam-transaction framework. + * @param logger In order to log thrown exceptions. + */ + private static void commit(SeamTransaction tx, Event txExceptionEvent, Logger logger) { + try { + switch (tx.getStatus()) { + case javax.transaction.Status.STATUS_ACTIVE: + tx.commit(); + break; + case javax.transaction.Status.STATUS_MARKED_ROLLBACK: + case javax.transaction.Status.STATUS_PREPARED: + case javax.transaction.Status.STATUS_PREPARING: + tx.rollback(); + break; + case javax.transaction.Status.STATUS_COMMITTED: + case javax.transaction.Status.STATUS_COMMITTING: + case javax.transaction.Status.STATUS_ROLLING_BACK: + case javax.transaction.Status.STATUS_UNKNOWN: + case javax.transaction.Status.STATUS_ROLLEDBACK: + case javax.transaction.Status.STATUS_NO_TRANSACTION: + break; + } + } catch (SystemException se) { + logger.warn("Error commiting/rolling back the transaction", se); + txExceptionEvent.fire(new ExceptionToCatch(se)); + } catch (HeuristicRollbackException hre) { + logger.warn("Error committing the transaction", hre); + txExceptionEvent.fire(new ExceptionToCatch(hre)); + } catch (RollbackException re) { + logger.warn("Error committing the transaction", re); + txExceptionEvent.fire(new ExceptionToCatch(re)); + } catch (HeuristicMixedException hme) { + logger.warn("Error committing the transaction", hme); + txExceptionEvent.fire(new ExceptionToCatch(hme)); + } + } + + private static void start(SeamTransaction tx, Event txExceptionEvent, Logger logger) { + try { + if (tx.getStatus() == Status.STATUS_ACTIVE) { + logger.warn("Transaction was already started before the listener"); + } else { + logger.debug("Beginning transaction"); + tx.begin(); + } + } catch (SystemException se) { + logger.warn("Error starting the transaction, or checking status", se); + txExceptionEvent.fire(new ExceptionToCatch(se)); + } catch (NotSupportedException e) { + logger.warn("Error starting the transaction", e); + txExceptionEvent.fire(new ExceptionToCatch(e)); + } + } +} diff --git a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/RuntimeResource.java b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/RuntimeResource.java index 0d2abcbd66..30f8626301 100644 --- a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/RuntimeResource.java +++ b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/RuntimeResource.java @@ -1,7 +1,5 @@ package org.kie.services.remote.rest; -import static org.kie.services.remote.util.CommandsRequestUtil.restProcessJaxbCommandsRequest; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -9,6 +7,7 @@ import java.util.Map.Entry; import javax.enterprise.context.RequestScoped; +import javax.enterprise.event.Event; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; @@ -32,7 +31,10 @@ import org.drools.core.process.instance.WorkItem; import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.InternalServerErrorException; -import org.jboss.resteasy.spi.UnsupportedMediaTypeException; +import org.jboss.resteasy.spi.NotAcceptableException; +import org.jboss.seam.transaction.DefaultTransaction; +import org.jboss.seam.transaction.SeamTransaction; +import org.jboss.solder.exception.control.ExceptionToCatch; import org.jbpm.process.audit.NodeInstanceLog; import org.jbpm.process.audit.ProcessInstanceLog; import org.jbpm.process.audit.VariableInstanceLog; @@ -42,8 +44,11 @@ import org.jbpm.process.audit.command.FindProcessInstancesCommand; import org.jbpm.process.audit.command.FindSubProcessInstancesCommand; import org.jbpm.process.audit.command.FindVariableInstancesCommand; +import org.jbpm.process.audit.event.AuditEvent; +import org.jbpm.services.task.commands.TaskCommand; import org.kie.api.command.Command; import org.kie.api.runtime.process.ProcessInstance; +import org.kie.services.client.api.command.AcceptedCommands; import org.kie.services.client.serialization.jaxb.impl.JaxbCommandsRequest; import org.kie.services.client.serialization.jaxb.impl.JaxbCommandsResponse; import org.kie.services.client.serialization.jaxb.impl.JaxbExceptionResponse; @@ -53,26 +58,33 @@ import org.kie.services.client.serialization.jaxb.impl.process.JaxbProcessInstanceWithVariablesResponse; import org.kie.services.client.serialization.jaxb.impl.process.JaxbWorkItem; import org.kie.services.client.serialization.jaxb.rest.JaxbGenericResponse; -import org.kie.services.remote.cdi.ProcessRequestBean; +import org.kie.services.remote.exception.KieRemoteServicesInternalError; +import org.kie.services.remote.exception.KieRemoteServicesPreConditionException; import org.kie.services.remote.util.Paginator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Path("/runtime/{id: [a-zA-Z0-9-:\\.]+}") @RequestScoped @SuppressWarnings("unchecked") public class RuntimeResource extends ResourceBase { - @Inject - private ProcessRequestBean processRequestBean; - - @PathParam("id") - private String deploymentId; + private static final Logger logger = LoggerFactory.getLogger(RuntimeResource.class); + /* REST information */ @Context private HttpServletRequest request; @Context private Request restRequest; + /* KIE information and processing */ + @Inject + private RestProcessRequestBean processRequestBean; + + @PathParam("id") + private String deploymentId; + // Rest methods -------------------------------------------------------------------------------------------------------------- @POST @@ -81,7 +93,7 @@ public class RuntimeResource extends ResourceBase { @Path("/execute") public JaxbCommandsResponse execute(JaxbCommandsRequest cmdsRequest) { return restProcessJaxbCommandsRequest(cmdsRequest, processRequestBean); - } + } @POST @Path("/process/{processDefId: [_a-zA-Z0-9-:\\.]+}/start") @@ -90,7 +102,13 @@ public Response startNewProcess(@PathParam("processDefId") String processId) { Map params = extractMapFromParams(formParams, "process/" + processId + "/start"); Command cmd = new StartProcessCommand(processId, params); - Object result = internalDoKieSessionOperation(cmd, "Unable to start process with process definition id '" + processId + "'"); + Object result = processRequestBean.doKieSessionOperation( + cmd, + deploymentId, + (Long) getNumberParam("processInstanceId", false, formParams, "process/"+ processId+"/start", true), + "Unable to start process with process definition id '" + processId + "'", + true); + JaxbProcessInstanceResponse responseObj = new JaxbProcessInstanceResponse((ProcessInstance) result, request); return createCorrectVariant(responseObj, restRequest); } @@ -101,7 +119,13 @@ public Response getProcessInstanceDetails(@PathParam("procInstId") Long procInst Command cmd = new GetProcessInstanceCommand(procInstId); ((GetProcessInstanceCommand) cmd).setReadOnly(true); - Object result = internalDoKieSessionOperation(cmd, "Unable to get process instance " + procInstId); + Object result = processRequestBean.doKieSessionOperation( + cmd, + deploymentId, + procInstId, + "Unable to get process instance " + procInstId, + true); + Object responseObj = null; if (result != null) { responseObj = new JaxbProcessInstanceResponse((ProcessInstance) result); @@ -117,7 +141,14 @@ public Response getProcessInstanceDetails(@PathParam("procInstId") Long procInst public Response abortProcessInstance(@PathParam("procInstId") Long procInstId) { Command cmd = new AbortProcessInstanceCommand(); ((AbortProcessInstanceCommand) cmd).setProcessInstanceId(procInstId); - internalDoKieSessionOperation(cmd, "Unable to abort process instance " + procInstId); + + processRequestBean.doKieSessionOperation( + cmd, + deploymentId, + procInstId, + "Unable to abort process instance " + procInstId, + true); + return createCorrectVariant(new JaxbGenericResponse(request), restRequest); } @@ -138,14 +169,15 @@ public Response signalProcessInstance(@PathParam("procInstId") Long procInstId) if( event != null ) { errorMsg += " and event '" + event + "'"; } - internalDoKieSessionOperation(cmd, errorMsg); + + processRequestBean.doKieSessionOperation(cmd, deploymentId, procInstId, errorMsg, true); return createCorrectVariant(new JaxbGenericResponse(request), restRequest); } @GET @Path("/process/instance/{procInstId: [0-9]+}/variables") public Response getProcessInstanceVariables(@PathParam("procInstId") Long procInstId) { - Map vars = getVariables(procInstId); + Map vars = getVariables(procInstId, true); return createCorrectVariant(new JaxbVariablesResponse(vars, request), restRequest); } @@ -155,20 +187,29 @@ public Response signalEvent() { Map> formParams = getRequestParams(request); String eventType = getStringParam("signal", true, formParams, "signal"); Object event = getObjectParam("event", false, formParams, "signal"); - Command cmd = new SignalEventCommand(eventType, event); String errorMsg = "Unable to send signal '" + eventType + "'"; if( event != null ) { errorMsg += " with event '" + event + "'"; } - internalDoKieSessionOperation(cmd, errorMsg); + + processRequestBean.doKieSessionOperation( + new SignalEventCommand(eventType, event), + deploymentId, + (Long) getNumberParam("processInstanceId", false, formParams, "signal", true), + errorMsg, + true); return createCorrectVariant(new JaxbGenericResponse(request), restRequest); } @GET @Path("/workitem/{workItemId: [0-9-]+}") public Response getWorkItem(@PathParam("workItemId") Long workItemId) { - Command cmd = new GetWorkItemCommand(workItemId); - WorkItem workItem = (WorkItem) internalDoKieSessionOperation(cmd, "Unable to get work item " + workItemId ); + WorkItem workItem = (WorkItem) processRequestBean.doKieSessionOperation( + new GetWorkItemCommand(workItemId), + deploymentId, + (Long) getNumberParam("processInstanceId", false, getRequestParams(request), "workitem/" + workItemId, true), + "Unable to get work item " + workItemId, + true); return createCorrectVariant(new JaxbWorkItem(workItem), restRequest); } @@ -185,7 +226,13 @@ public Response doWorkItemOperation(@PathParam("workItemId") Long workItemId, @P } else { throw new BadRequestException("Unsupported operation: /process/instance/" + workItemId + "/" + operation); } - internalDoKieSessionOperation(cmd, "Unable to " + operation + " workitem " + workItemId ); + + processRequestBean.doKieSessionOperation( + cmd, + deploymentId, + (Long) getNumberParam("processInstanceId", false, params, "workitem/" + workItemId + "/" + operation, true), + "Unable to " + operation + " work item " + workItemId, + true); return createCorrectVariant(new JaxbGenericResponse(request), restRequest); } @@ -196,8 +243,12 @@ public Response doWorkItemOperation(@PathParam("workItemId") Long workItemId, @P @POST @Path("/history/clear") public Response clearProcessInstanceLogs() { - Command cmd = new ClearHistoryLogsCommand(); - internalDoKieSessionOperation(cmd, "Unable to clear process instance logs"); + processRequestBean.doKieSessionOperation( + new ClearHistoryLogsCommand(), + deploymentId, + (Long) getNumberParam("processInstanceId", false, getRequestParams(request), "history/clear", true), + "Unable to clear process instance logs", + true); return createCorrectVariant(new JaxbGenericResponse(request), restRequest); } @@ -207,8 +258,12 @@ public Response getProcessInstanceLogs() { Map> params = getRequestParams(request); int [] pageInfo = getPageNumAndPageSize(params); - Command cmd = new FindProcessInstancesCommand(); - Object result = internalDoKieSessionOperation(cmd, "Unable to get process instance logs"); + Object result = processRequestBean.doKieSessionOperation( + new FindProcessInstancesCommand(), + deploymentId, + (Long) getNumberParam("processInstanceId", false, params, "history/clear", true), + "Unable to get process instance logs", + true); List results = (List) result; results = (new Paginator()).paginate(pageInfo, results); @@ -221,8 +276,12 @@ public Response getSpecificProcessInstanceLogs(@PathParam("procInstId") long pro Map> params = getRequestParams(request); int [] pageInfo = getPageNumAndPageSize(params); - Command cmd = new FindProcessInstanceCommand(procInstId); - Object result = internalDoKieSessionOperation(cmd, "Unable to get process instance logs for process instance " + procInstId); + Object result = processRequestBean.doKieSessionOperation( + new FindProcessInstanceCommand(procInstId), + deploymentId, + procInstId, + "Unable to get process instance logs for process instance " + procInstId, + true); ProcessInstanceLog procInstLog = (ProcessInstanceLog) result; List logList = new ArrayList(); @@ -234,34 +293,29 @@ public Response getSpecificProcessInstanceLogs(@PathParam("procInstId") long pro @GET @Path("/history/instance/{procInstId: [0-9]+}/{oper: [a-zA-Z]+}") - public Response getVariableOrNodeHistoryList(@PathParam("procInstId") Long procInstId, - @PathParam("oper") String operation) { + public Response getVariableOrNodeHistoryList(@PathParam("procInstId") Long procInstId, @PathParam("oper") String operation) { Map> params = getRequestParams(request); int [] pageInfo = getPageNumAndPageSize(params); - JaxbHistoryLogList resultList; Command cmd; + String errorMsg; if ("child".equalsIgnoreCase(operation)) { cmd = new FindSubProcessInstancesCommand(procInstId); - Object result = internalDoKieSessionOperation(cmd, "Unable to get child process instance logs for process instance " + procInstId); - List procInstLogList = (List) result; - procInstLogList = (new Paginator()).paginate(pageInfo, procInstLogList); - resultList = new JaxbHistoryLogList(procInstLogList); + errorMsg = "Unable to get child process instance logs for process instance " + procInstId; } else if ("node".equalsIgnoreCase(operation)) { cmd = new FindNodeInstancesCommand(procInstId); - Object result = internalDoKieSessionOperation(cmd, "Unable to get node instance logs for process instance " + procInstId); - List nodeInstLogList = (List) result; - nodeInstLogList = (new Paginator()).paginate(pageInfo, nodeInstLogList); - resultList = new JaxbHistoryLogList(nodeInstLogList); + errorMsg = "Unable to get node instance logs for process instance " + procInstId; } else if ("variable".equalsIgnoreCase(operation)) { cmd = new FindVariableInstancesCommand(procInstId); - Object result = internalDoKieSessionOperation(cmd, "Unable to get variable instance logs for process instance " + procInstId); - List varInstLogList = (List) result; - varInstLogList = (new Paginator()).paginate(pageInfo, varInstLogList); - resultList = new JaxbHistoryLogList(varInstLogList); + errorMsg = "Unable to get variable instance logs for process instance " + procInstId; } else { throw new BadRequestException("Unsupported operation: /history/instance/" + procInstId + "/" + operation); } + + Object result = processRequestBean.doKieSessionOperation(cmd, deploymentId, procInstId, errorMsg, true); + List varInstLogList = (List) result; + varInstLogList = (new Paginator()).paginate(pageInfo, varInstLogList); + JaxbHistoryLogList resultList = new JaxbHistoryLogList(varInstLogList); return createCorrectVariant(resultList, restRequest); } @@ -272,24 +326,23 @@ public Response getSpecificVariableOrNodeHistoryList(@PathParam("procInstId") Lo Map> params = getRequestParams(request); int [] pageInfo = getPageNumAndPageSize(params); - JaxbHistoryLogList resultList; Command cmd; + String errorMsg; if ("node".equalsIgnoreCase(operation)) { cmd = new FindNodeInstancesCommand(procInstId, logId); - Object result = internalDoKieSessionOperation(cmd, "Unable to get node instance logs for node '" + logId + "' in process instance " + procInstId); - List nodeInstLogList = (List) result; - nodeInstLogList = (new Paginator()).paginate(pageInfo, nodeInstLogList); - resultList = new JaxbHistoryLogList(nodeInstLogList); + errorMsg ="Unable to get node instance logs for node '" + logId + "' in process instance " + procInstId; } else if ("variable".equalsIgnoreCase(operation)) { cmd = new FindVariableInstancesCommand(procInstId, logId); - Object result = internalDoKieSessionOperation(cmd, "Unable to get variable instance logs for variable '" + logId + "' in process instance " + procInstId); - List varInstLogList = (List) result; - varInstLogList = (new Paginator()).paginate(pageInfo, varInstLogList); - resultList = new JaxbHistoryLogList(varInstLogList); + errorMsg = "Unable to get variable instance logs for variable '" + logId + "' in process instance " + procInstId; } else { throw new BadRequestException("Unsupported operation: /history/instance/" + procInstId + "/" + operation + "/" + logId); } + + Object result = processRequestBean.doKieSessionOperation(cmd, deploymentId, procInstId, errorMsg, true); + List varInstLogList = (List) result; + varInstLogList = (new Paginator()).paginate(pageInfo, varInstLogList); + JaxbHistoryLogList resultList = new JaxbHistoryLogList(varInstLogList); return createCorrectVariant(resultList, restRequest); } @@ -299,8 +352,12 @@ public Response getProcessInstanceLogs(@PathParam("procId") String processId) { Map> params = getRequestParams(request); int [] pageInfo = getPageNumAndPageSize(params); - Command cmd = new FindProcessInstancesCommand(processId); - Object result = internalDoKieSessionOperation(cmd, "Unable to get process instance logs for process '" + processId + "'"); + Object result = processRequestBean.doKieSessionOperation( + new FindProcessInstancesCommand(processId), + deploymentId, + (Long) getNumberParam("processInstanceId", false, params, "history/process/" + processId, true), + "Unable to get process instance logs for process '" + processId + "'", + true); List procInstLogList = (List) result; procInstLogList = (new Paginator()).paginate(pageInfo, procInstLogList); @@ -316,12 +373,17 @@ public Response getProcessInstanceLogs(@PathParam("procId") String processId) { public Response startNewProcessWithVars(@PathParam("processDefId") String processId) { Map> formParams = getRequestParams(request); Map params = extractMapFromParams(formParams, "process/" + processId + "/start"); - Command cmd = new StartProcessCommand(processId, params); - Object result = internalDoKieSessionOperation(cmd, "Unable to get process instance logs for process '" + processId + "'"); + Object result = processRequestBean.doKieSessionOperation( + new StartProcessCommand(processId, params), + deploymentId, + (Long) getNumberParam("processInstanceId", false, formParams, "withvars/process/" + processId + "/start", true), + "Unable to get process instance logs for process '" + processId + "'", + false); + ProcessInstance procInst = (ProcessInstance) result; - Map vars = getVariables(procInst.getId()); + Map vars = getVariables(procInst.getId(), true); JaxbProcessInstanceWithVariablesResponse resp = new JaxbProcessInstanceWithVariablesResponse(procInst, vars, request); return createCorrectVariant(resp, restRequest); @@ -332,11 +394,18 @@ public Response startNewProcessWithVars(@PathParam("processDefId") String proces public Response getProcessInstanceWithVars(@PathParam("procInstId") Long procInstId) { Command cmd = new GetProcessInstanceCommand(procInstId); ((GetProcessInstanceCommand) cmd).setReadOnly(true); + + Object result = processRequestBean.doKieSessionOperation( + cmd, + deploymentId, + procInstId, + "Unable to get process instance " + procInstId, + false); + JaxbProcessInstanceWithVariablesResponse responseObj = null; - Object result = internalDoKieSessionOperation(cmd, "Unable to get process instance " + procInstId); if (result != null) { ProcessInstance procInst = (ProcessInstance) result; - Map vars = getVariables(procInstId); + Map vars = getVariables(procInstId, true); responseObj = new JaxbProcessInstanceWithVariablesResponse(procInst, vars, request); } else { throw new BadRequestException("Unable to retrieve process instance " + procInstId @@ -351,7 +420,6 @@ public Response signalProcessInstanceWithVars(@PathParam("procInstId") Long proc Map> params = getRequestParams(request); String eventType = getStringParam("eventType", true, params, "signal"); Object event = getObjectParam("event", false, params, "signal"); - Command cmd = new SignalEventCommand(procInstId, eventType, event); String errorMsg = "Unable to signal process instance " + procInstId; if( eventType == null ) { errorMsg += " with empty signal"; @@ -361,14 +429,27 @@ public Response signalProcessInstanceWithVars(@PathParam("procInstId") Long proc if( event != null ) { errorMsg += " and event '" + event + "'"; } - internalDoKieSessionOperation(cmd, errorMsg); - cmd = new GetProcessInstanceCommand(procInstId); + processRequestBean.doKieSessionOperation( + new SignalEventCommand(procInstId, eventType, event), + deploymentId, + procInstId, + errorMsg, + false); + + Command cmd = new GetProcessInstanceCommand(procInstId); ((GetProcessInstanceCommand) cmd).setReadOnly(true); - Object result = internalDoKieSessionOperation(cmd, "Unable to get process instance " + procInstId); + Object result = processRequestBean.doKieSessionOperation( + cmd, + deploymentId, + procInstId, + "Unable to get process instance " + procInstId, + false); ProcessInstance processInstance = (ProcessInstance) result; - - Map vars = getVariables(processInstance.getId()); + if( processInstance == null ) { + throw new KieRemoteServicesPreConditionException("This method can only be used on processes that will not complete after a signal."); + } + Map vars = getVariables(processInstance.getId(), true); return createCorrectVariant(new JaxbProcessInstanceWithVariablesResponse(processInstance, vars), restRequest); @@ -376,23 +457,13 @@ public Response signalProcessInstanceWithVars(@PathParam("procInstId") Long proc // Helper methods -------------------------------------------------------------------------------------------------------------- - private Object internalDoKieSessionOperation(Command cmd, String errorMsg) { - Object result = processRequestBean.doKieSessionOperation(cmd, deploymentId, null); - if( result instanceof JaxbExceptionResponse ) { - Exception e = ((JaxbExceptionResponse) result).getCause(); - if( e instanceof RuntimeException ) { - throw (RuntimeException) e; - } else { - throw new InternalServerErrorException(errorMsg, e); - } - } - return result; - } - - private Map getVariables(long processInstanceId) { - Command cmd = new FindVariableInstancesCommand(processInstanceId); - - Object result = internalDoKieSessionOperation(cmd, "Unable to retrieve process variables from process instance " + processInstanceId); + private Map getVariables(long processInstanceId, boolean lastOperation) { + Object result = processRequestBean.doKieSessionOperation( + new FindVariableInstancesCommand(processInstanceId), + deploymentId, + processInstanceId, + "Unable to retrieve process variables from process instance " + processInstanceId, + lastOperation); List varInstLogList = (List) result; Map vars = new HashMap(); @@ -417,4 +488,6 @@ private Map getVariables(long processInstanceId) { return vars; } + + } diff --git a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/TaskResource.java b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/TaskResource.java index 0a9006126a..53758309c0 100644 --- a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/TaskResource.java +++ b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/TaskResource.java @@ -1,7 +1,5 @@ package org.kie.services.remote.rest; -import static org.kie.services.remote.util.CommandsRequestUtil.restProcessJaxbCommandsRequest; - import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; @@ -26,7 +24,6 @@ import javax.ws.rs.core.UriInfo; import org.jboss.resteasy.spi.BadRequestException; -import org.jboss.resteasy.spi.InternalServerErrorException; import org.jboss.resteasy.spi.NotFoundException; import org.jbpm.kie.services.api.IdentityProvider; import org.jbpm.services.task.commands.*; @@ -42,10 +39,9 @@ import org.kie.api.task.model.TaskSummary; import org.kie.services.client.serialization.jaxb.impl.JaxbCommandsRequest; import org.kie.services.client.serialization.jaxb.impl.JaxbCommandsResponse; -import org.kie.services.client.serialization.jaxb.impl.JaxbExceptionResponse; import org.kie.services.client.serialization.jaxb.impl.task.JaxbTaskSummaryListResponse; import org.kie.services.client.serialization.jaxb.rest.JaxbGenericResponse; -import org.kie.services.remote.cdi.ProcessRequestBean; +import org.kie.services.remote.exception.KieRemoteServicesInternalError; import org.kie.services.remote.util.Paginator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,7 +53,7 @@ public class TaskResource extends ResourceBase { private static final Logger logger = LoggerFactory.getLogger(RuntimeResource.class); @Inject - private ProcessRequestBean processRequestBean; + private RestProcessRequestBean processRequestBean; @Context private HttpServletRequest request; @@ -152,7 +148,7 @@ public Response query(@Context UriInfo uriInfo) { List statusList = convertStringListToStatusList(statusStrList); // process params/cmds - Queue> cmds = new LinkedList>(); + Queue> cmds = new LinkedList>(); if (!workItemIdList.isEmpty()) { for (Long workItemId : workItemIdList) { cmds.add(new GetTaskByWorkItemIdCommand(workItemId)); @@ -167,11 +163,13 @@ public Response query(@Context UriInfo uriInfo) { Set alreadyRetrievedSet = new HashSet(); List results = new ArrayList(); - Command cmd = null; + TaskCommand cmd = null; while (!cmds.isEmpty()) { cmd = cmds.poll(); logger.debug( "query: " + cmd.getClass().getSimpleName()); - TaskImpl task = (TaskImpl) internalDoTaskOperation(cmd, "Unable to execute " + cmd.getClass().getSimpleName()); + TaskImpl task = (TaskImpl) processRequestBean.doTaskOperation( + cmd, + "Unable to execute " + cmd.getClass().getSimpleName()); if (task != null) { TaskSummaryImpl taskSum = convertTaskToTaskSummary(task); if( alreadyRetrievedSet.add(taskSum) ) { @@ -202,10 +200,14 @@ public Response query(@Context UriInfo uriInfo) { cmd = new GetTasksByProcessInstanceIdCommand(procInstId); logger.debug( "query: " + cmd.getClass().getSimpleName()); @SuppressWarnings("unchecked") - List procInstTaskIdList = (List) internalDoTaskOperation(cmd, "Unable to execute " + cmd.getClass().getSimpleName()); + List procInstTaskIdList = (List) processRequestBean.doTaskOperation( + cmd, + "Unable to execute " + cmd.getClass().getSimpleName()); for (Long taskId : procInstTaskIdList) { cmd = new GetTaskCommand(taskId); - TaskImpl task = (TaskImpl) internalDoTaskOperation(cmd, "Unable to execute " + cmd.getClass().getSimpleName()); + TaskImpl task = (TaskImpl) processRequestBean.doTaskOperation( + cmd, + "Unable to execute " + cmd.getClass().getSimpleName()); if (task != null) { TaskSummaryImpl taskSum = convertTaskToTaskSummary(task); if( alreadyRetrievedSet.add(taskSum) ) { @@ -256,7 +258,9 @@ public Response query(@Context UriInfo uriInfo) { cmd = cmds.poll(); logger.debug( "query: " + cmd.getClass().getSimpleName()); @SuppressWarnings("unchecked") - List taskSummaryList = (List) internalDoTaskOperation(cmd, "Unable to execute " + cmd.getClass().getSimpleName()); + List taskSummaryList = (List) processRequestBean.doTaskOperation( + cmd, + "Unable to execute " + cmd.getClass().getSimpleName()); if (taskSummaryList != null && !taskSummaryList.isEmpty()) { for (TaskSummary taskSummary : taskSummaryList) { TaskSummaryImpl taskSum = (TaskSummaryImpl) taskSummary; @@ -280,8 +284,10 @@ public Response query(@Context UriInfo uriInfo) { @GET @Path("/{taskId: [0-9-]+}") public Response getTaskInstanceInfo(@PathParam("taskId") long taskId) { - Command cmd = new GetTaskCommand(taskId); - Task task = (Task) internalDoTaskOperation(cmd, "Unable to get task " + taskId); + TaskCommand cmd = new GetTaskCommand(taskId); + Task task = (Task) processRequestBean.doTaskOperation( + cmd, + "Unable to get task " + taskId); if( task == null ) { throw new NotFoundException("Task " + taskId + " could not be found."); } @@ -297,11 +303,8 @@ public Response doTaskOperation(@PathParam("taskId") long taskId, @PathParam("op String userId = identityProvider.getName(); logger.debug("Executing " + operation + " on task " + taskId + " by user " + userId ); - Command cmd = null; - cmd = new GetTaskCommand(taskId); - if( internalDoTaskOperation(cmd, "Unable to check if task " + taskId + " exists") == null ) { - throw new NotFoundException("Task " + taskId + " could not be found."); - } + TaskCommand cmd = null; + if ("activate".equalsIgnoreCase(operation)) { cmd = new ActivateTaskCommand(taskId, userId); } else if ("claim".equalsIgnoreCase(operation)) { @@ -344,15 +347,49 @@ public Response doTaskOperation(@PathParam("taskId") long taskId, @PathParam("op } else { throw new BadRequestException("Unsupported operation: /task/" + taskId + "/" + operation); } - internalDoTaskOperation(cmd, "Unable to " + operation + " task " + taskId); + + internalCheckAndDoTaskOperation(cmd, taskId, "Unable to " + operation + " task " + taskId); return createCorrectVariant(new JaxbGenericResponse(request), restRequest); } + private static String checkThatOperationExists(String operation, String[] possibleOperations) { + for (String oper : possibleOperations) { + if (oper.equals(operation.trim().toLowerCase())) { + return oper; + } + } + throw new BadRequestException("Operation '" + operation + "' is not supported on tasks."); + } + + private Object internalCheckAndDoTaskOperation(TaskCommand cmd, Long taskId, String errorMsg) { + assert taskId != null : "Submitted task id should always have a value."; + + if( cmd instanceof CompleteTaskCommand + || cmd instanceof ExitTaskCommand + || cmd instanceof FailTaskCommand + || cmd instanceof SkipTaskCommand ) { + TaskCommand getTaskCmd = new GetTaskCommand(taskId); + Task task = (Task) processRequestBean.doTaskOperation( + getTaskCmd, + "Task " + taskId + " does not exist or unable to check if it exists"); + if( task == null ) { + throw new NotFoundException("Task " + taskId + " could not be found."); + } + String deploymentId = task.getTaskData().getDeploymentId(); + return processRequestBean.doTaskOperationOnDeployment(cmd, errorMsg, deploymentId); + } else { + return processRequestBean.doTaskOperation(cmd, errorMsg); + } + } + + @GET @Path("/{taskId: [0-9-]+}/content") public Response getTaskContent(@PathParam("taskId") long taskId) { - Command cmd = new GetTaskCommand(taskId); - Object result = internalDoTaskOperation(cmd, "Unable to get task " + taskId); + TaskCommand cmd = new GetTaskCommand(taskId); + Object result = processRequestBean.doTaskOperation( + cmd, + "Unable to get task " + taskId); if( result == null ) { throw new NotFoundException("Task " + taskId + " could not be found."); } @@ -360,7 +397,9 @@ public Response getTaskContent(@PathParam("taskId") long taskId) { Content content = null; if( contentId > -1 ) { cmd = new GetContentCommand(contentId); - result = internalDoTaskOperation(cmd, "Unable get content " + contentId + " (from task " + taskId + ")"); + result = processRequestBean.doTaskOperation( + cmd, + "Unable get content " + contentId + " (from task " + taskId + ")"); content = (Content) result; } return createCorrectVariant(new JaxbContent(content), restRequest); @@ -369,27 +408,12 @@ public Response getTaskContent(@PathParam("taskId") long taskId) { @GET @Path("/content/{contentId: [0-9-]+}") public Response getContent(@PathParam("contentId") long contentId) { - Command cmd = new GetContentCommand(contentId); - Content content = (Content) internalDoTaskOperation(cmd, "Unable to get task content " + contentId); + TaskCommand cmd = new GetContentCommand(contentId); + Content content = (Content) processRequestBean.doTaskOperation(cmd, "Unable to get task content " + contentId); if( content == null ) { throw new NotFoundException("Content " + contentId + " could not be found."); } return createCorrectVariant(new JaxbContent(content), restRequest); } - // Helper methods -------------------------------------------------------------------------------------------------------------- - - private Object internalDoTaskOperation(Command cmd, String errorMsg) { - Object result = processRequestBean.doTaskOperation(cmd); - if( result instanceof JaxbExceptionResponse ) { - Exception cause = ((JaxbExceptionResponse) result).getCause(); - if( cause instanceof RuntimeException ) { - throw (RuntimeException) cause; - } else { - throw new InternalServerErrorException(errorMsg, cause); - } - } - return result; - } - } diff --git a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/exception/DescriptiveExceptionHandler.java b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/exception/DescriptiveExceptionHandler.java index 4692198069..abe500b4f0 100644 --- a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/exception/DescriptiveExceptionHandler.java +++ b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/rest/exception/DescriptiveExceptionHandler.java @@ -1,7 +1,9 @@ package org.kie.services.remote.rest.exception; +import static org.kie.services.remote.rest.ResourceBase.variants; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Context; +import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.ext.ExceptionMapper; @@ -24,6 +26,9 @@ public class DescriptiveExceptionHandler implements ExceptionMapper { @Context HttpServletRequest request; + + @Context + Request restRequest; @Override public Response toResponse(Exception e) { @@ -57,6 +62,7 @@ public Response toResponse(Exception e) { } catch (JAXBException jaxb) { responseBuilder.entity(JaxbGenericResponse.convertStackTraceToString(jaxb)); } + responseBuilder.variant(restRequest.selectVariant(variants)); return responseBuilder.build(); } diff --git a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/util/CommandsRequestUtil.java b/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/util/CommandsRequestUtil.java deleted file mode 100644 index 8ea93e353f..0000000000 --- a/kie-remote/kie-services-remote/src/main/java/org/kie/services/remote/util/CommandsRequestUtil.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.kie.services.remote.util; - -import java.util.List; - -import org.jboss.resteasy.spi.NotAcceptableException; -import org.jbpm.services.task.commands.TaskCommand; -import org.kie.api.command.Command; -import org.kie.services.client.api.command.AcceptedCommands; -import org.kie.services.client.serialization.jaxb.impl.JaxbCommandsRequest; -import org.kie.services.client.serialization.jaxb.impl.JaxbCommandsResponse; -import org.kie.services.client.serialization.jaxb.impl.JaxbExceptionResponse; -import org.kie.services.remote.cdi.ProcessRequestBean; -import org.kie.services.remote.exception.KieRemoteServicesInternalError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class CommandsRequestUtil { - - private static final Logger logger = LoggerFactory.getLogger(CommandsRequestUtil.class); - - public static JaxbCommandsResponse restProcessJaxbCommandsRequest(JaxbCommandsRequest request, ProcessRequestBean requestBean) { - // If exceptions are happening here, then there is something REALLY wrong and they should be thrown. - JaxbCommandsResponse jaxbResponse = new JaxbCommandsResponse(request); - List> commands = request.getCommands(); - - if (commands != null) { - for (int i = 0; i < commands.size(); ++i) { - Command cmd = commands.get(i); - if (!AcceptedCommands.getSet().contains(cmd.getClass())) { - throw new NotAcceptableException("The execute REST operation does not accept " + cmd.getClass().getName() - + " instances."); - } - logger.debug("Processing command " + cmd.getClass().getSimpleName()); - Object cmdResult = null; - if (cmd instanceof TaskCommand) { - cmdResult = requestBean.doTaskOperation(cmd); - } else { - cmdResult = requestBean.doKieSessionOperation(cmd, request.getDeploymentId(), request.getProcessInstanceId()); - } - if (cmdResult instanceof JaxbExceptionResponse) { - Exception e = ((JaxbExceptionResponse) cmdResult).cause; - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } else { - throw new KieRemoteServicesInternalError("Unable to execute " + cmd.getClass().getSimpleName() + ": " - + e.getMessage(), e); - } - } - if (cmdResult != null) { - try { - // addResult could possibly throw an exception, which is why it's here and not above - jaxbResponse.addResult(cmdResult, i, cmd); - } catch (Exception e) { - logger.error("Unable to add result from " + cmd.getClass().getSimpleName() + "/" + i + " because of " - + e.getClass().getSimpleName(), e); - jaxbResponse.addException(e, i, cmd); - } - } - } - } - - if (commands == null || commands.isEmpty()) { - logger.info("Commands request object with no commands sent!"); - } - - return jaxbResponse; - } - - -}