Java tutorial
/* * Copyright 2013 JBoss by Red Hat. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jbpm.executor.impl; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.input.ClassLoaderObjectInputStream; import org.apache.commons.lang3.exception.ExceptionUtils; import org.jbpm.executor.entities.ErrorInfo; import org.jbpm.executor.entities.RequestInfo; import org.kie.api.executor.Command; import org.kie.api.executor.CommandCallback; import org.kie.api.executor.CommandContext; import org.kie.api.executor.ExecutionResults; import org.kie.api.executor.ExecutorQueryService; import org.kie.api.executor.ExecutorStoreService; import org.kie.api.executor.Reoccurring; import org.kie.api.executor.STATUS; import org.kie.internal.runtime.manager.InternalRuntimeManager; import org.kie.internal.runtime.manager.RuntimeManagerRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Heart of the executor component - executes the actual tasks. * Handles retries and error management. Based on results of execution notifies * defined callbacks about the execution results. * */ public abstract class AbstractAvailableJobsExecutor { private static final Logger logger = LoggerFactory.getLogger(AbstractAvailableJobsExecutor.class); protected int retries = Integer.parseInt(System.getProperty("org.kie.executor.retry.count", "3")); protected Map<String, Object> contextData = new HashMap<String, Object>(); protected ExecutorQueryService queryService; protected ClassCacheManager classCacheManager; protected ExecutorStoreService executorStoreService; public void setQueryService(ExecutorQueryService queryService) { this.queryService = queryService; } public void setClassCacheManager(ClassCacheManager classCacheManager) { this.classCacheManager = classCacheManager; } public void setExecutorStoreService(ExecutorStoreService executorStoreService) { this.executorStoreService = executorStoreService; } public void executeGivenJob(RequestInfo request) { try { if (request != null) { boolean processReoccurring = false; Command cmd = null; CommandContext ctx = null; List<CommandCallback> callbacks = null; ClassLoader cl = getClassLoader(request.getDeploymentId()); try { logger.debug("Processing Request Id: {}, status {} command {}", request.getId(), request.getStatus(), request.getCommandName()); byte[] reqData = request.getRequestData(); if (reqData != null) { ObjectInputStream in = null; try { in = new ClassLoaderObjectInputStream(cl, new ByteArrayInputStream(reqData)); ctx = (CommandContext) in.readObject(); } catch (IOException e) { logger.warn("Exception while serializing context data", e); return; } finally { if (in != null) { in.close(); } } } for (Map.Entry<String, Object> entry : contextData.entrySet()) { ctx.setData(entry.getKey(), entry.getValue()); } // add class loader so internally classes can be created with valid (kjar) deployment ctx.setData("ClassLoader", cl); cmd = classCacheManager.findCommand(request.getCommandName(), cl); ExecutionResults results = cmd.execute(ctx); callbacks = classCacheManager.buildCommandCallback(ctx, cl); for (CommandCallback handler : callbacks) { handler.onCommandDone(ctx, results); } if (results != null) { try { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bout); out.writeObject(results); byte[] respData = bout.toByteArray(); request.setResponseData(respData); } catch (IOException e) { request.setResponseData(null); } } request.setStatus(STATUS.DONE); executorStoreService.updateRequest(request); processReoccurring = true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (Throwable e) { callbacks = classCacheManager.buildCommandCallback(ctx, cl); processReoccurring = handleException(request, e, ctx, callbacks); } finally { handleCompletion(processReoccurring, cmd, ctx); } } } catch (Exception e) { logger.warn("Unexpected error while processin executor's job {}", e.getMessage(), e); } } protected ClassLoader getClassLoader(String deploymentId) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (deploymentId == null) { return cl; } InternalRuntimeManager manager = ((InternalRuntimeManager) RuntimeManagerRegistry.get() .getManager(deploymentId)); if (manager != null && manager.getEnvironment().getClassLoader() != null) { cl = manager.getEnvironment().getClassLoader(); } return cl; } public void addContextData(String name, Object data) { this.contextData.put(name, data); } @SuppressWarnings("unchecked") protected boolean handleException(RequestInfo request, Throwable e, CommandContext ctx, List<CommandCallback> callbacks) { logger.warn("Error during command {} error message {}", request.getCommandName(), e.getMessage(), e); ErrorInfo errorInfo = new ErrorInfo(e.getMessage(), ExceptionUtils.getStackTrace(e.fillInStackTrace())); errorInfo.setRequestInfo(request); ((List<ErrorInfo>) request.getErrorInfo()).add(errorInfo); logger.debug("Error Number: {}", request.getErrorInfo().size()); if (request.getRetries() > 0) { request.setStatus(STATUS.RETRYING); request.setRetries(request.getRetries() - 1); // calculate next retry time List<Long> retryDelay = (List<Long>) ctx.getData("retryDelay"); if (retryDelay != null) { long retryAdd = 0l; try { retryAdd = retryDelay.get(request.getExecutions()); } catch (IndexOutOfBoundsException ex) { // in case there is no element matching given execution, use last one retryAdd = retryDelay.get(retryDelay.size() - 1); } request.setTime(new Date(System.currentTimeMillis() + retryAdd)); request.setExecutions(request.getExecutions() + 1); logger.info("Retrying request ( with id {}) - delay configured, next retry at {}", request.getId(), request.getTime()); } logger.debug("Retrying ({}) still available!", request.getRetries()); executorStoreService.updateRequest(request); return false; } else { logger.debug("Error no retries left!"); request.setStatus(STATUS.ERROR); request.setExecutions(request.getExecutions() + 1); executorStoreService.updateRequest(request); if (callbacks != null) { for (CommandCallback handler : callbacks) { handler.onCommandError(ctx, e); } } return true; } } protected void handleCompletion(boolean processReoccurring, Command cmd, CommandContext ctx) { if (processReoccurring && cmd != null && cmd instanceof Reoccurring) { Date current = new Date(); Date nextScheduleTime = ((Reoccurring) cmd).getScheduleTime(); if (nextScheduleTime != null && nextScheduleTime.after(current)) { String businessKey = (String) ctx.getData("businessKey"); RequestInfo requestInfo = new RequestInfo(); requestInfo.setCommandName(cmd.getClass().getName()); requestInfo.setKey(businessKey); requestInfo.setStatus(STATUS.QUEUED); requestInfo.setTime(nextScheduleTime); requestInfo.setMessage("Rescheduled reoccurring job"); requestInfo.setDeploymentId((String) ctx.getData("deploymentId")); requestInfo.setOwner((String) ctx.getData("owner")); if (ctx.getData("retries") != null) { requestInfo.setRetries(Integer.valueOf(String.valueOf(ctx.getData("retries")))); } else { requestInfo.setRetries(retries); } if (ctx != null) { try { // remove transient data ctx.getData().remove("ClassLoader"); ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream oout = new ObjectOutputStream(bout); oout.writeObject(ctx); requestInfo.setRequestData(bout.toByteArray()); } catch (IOException e) { logger.warn("Error serializing context data", e); requestInfo.setRequestData(null); } } executorStoreService.persistRequest(requestInfo); } } } }