package hudson.drools; import hudson.Functions; import hudson.drools.renderer.RuleFlowRenderer; import hudson.model.BallColor; import hudson.model.BuildListener; import hudson.model.Cause; import hudson.model.CauseAction; import hudson.model.Hudson; import hudson.model.Job; import hudson.model.Queue; import hudson.model.Result; import hudson.model.Run; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.xml.xpath.XPathExpressionException; import org.dom4j.DocumentException; import org.drools.runtime.process.ProcessInstance; import org.drools.runtime.process.WorkflowProcessInstance; import org.kohsuke.stapler.HttpRedirect; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; public class DroolsRun extends Run<DroolsProject, DroolsRun> implements Queue.Executable { private static Logger logger = Logger.getLogger(DroolsRun.class.getName()); private List<HumanTask> humanTasks; private List<ScriptExecution> scriptExecutions; private List<NodeInstanceLog> logs; private transient PrintWriter logWriter; private long processInstanceId; private String processXML; private Object readResolve() { for (HumanTask task : humanTasks) { task.setRun(this); } return this; } protected DroolsRun(DroolsProject project) throws IOException { super(project); processXML = project.getProcessXML(); humanTasks = new CopyOnWriteArrayList<HumanTask>(); status = Status.STARTED; scriptExecutions = new CopyOnWriteArrayList<ScriptExecution>(); logs = new CopyOnWriteArrayList<NodeInstanceLog>(); } public DroolsRun(DroolsProject project, File dir) throws IOException { super(project, dir); } public List<HumanTask> getHumanTasks() { return humanTasks; } public void addHumanTask(HumanTask humanTask) { humanTasks.add(humanTask); humanTask.setRun(this); try { save(); } catch (IOException e) { throw new RuntimeException("Could not save!", e); } } // needs to be an int to please Stapler public HumanTask getHumanTask(int workItemId) { for (HumanTask task : humanTasks) { if (workItemId == task.getWorkItemId()) { return task; } } return null; } /* * We need two strategies two find the DroolsRun. When the process is * starting, the DroolsRun does not now it processInstanceId yet, so we * query the process variable "run". * * After the process is completed, the processInstance or variable will be * gone, so we need to iterate over all the builds to find the right one. * public static DroolsRun getFromProcessInstance(long processInstanceId) { * DroolsRun result = null; ProcessInstance processInstance = * PluginImpl.getInstance().getSession() * .getProcessInstance(processInstanceId); if (processInstance != null) { * result = getFromProcessInstance(processInstance); } if (result == null) { * // probably because the workflow has been completed for (Item item : * Hudson.getInstance().getItemMap().values()) { if (item instanceof * DroolsProject) for (DroolsRun run : ((DroolsProject) item).getBuilds()) { * if (run.getProcessInstanceId() == processInstanceId) { return run; } } } * } return result; } */ public static DroolsRun getFromProcessInstance( ProcessInstance processInstance) { RunWrapper wrapper = (RunWrapper) ((WorkflowProcessInstance) processInstance) .getVariable(Constants.RUN); if (wrapper == null) { return null; } DroolsRun run = (DroolsRun) wrapper.getRun(); return run; } public List<NodeInstanceLog> getLogs() { return logs; } public void run() { run(new RunnerImpl()); } protected class RunnerImpl extends Runner { public void cleanUp(BuildListener listener) throws Exception { } public void post(BuildListener listener) throws Exception { } public Result run(BuildListener listener) throws Exception, hudson.model.Run.RunnerAbortedException { CauseAction cause = getAction(CauseAction.class); if (cause != null) { PrintWriter logWriter = getLogWriter(); for (Cause c: cause.getCauses()) { logWriter.println(c.getShortDescription()); } } ProcessInstance instance = getParent().run( new StartProcessCallable(DroolsRun.this, getParent() .getProcessId())); if (instance != null) { processInstanceId = instance.getId(); if (instance.getState() != ProcessInstance.STATE_ABORTED) { return Result.SUCCESS; } } return Result.FAILURE; } } public String getUpUrl() { return Functions.getNearestAncestorUrl(Stapler.getCurrentRequest(), getParent()) + '/'; } public synchronized RuleFlowRenderer getRuleFlowRenderer() { return new RuleFlowRenderer(processXML, getLogs()); } public void doProcessInstanceImage(StaplerRequest req, StaplerResponse rsp) throws IOException, XPathExpressionException, DocumentException { ServletOutputStream output = rsp.getOutputStream(); rsp.setContentType("image/png"); getRuleFlowRenderer().write(output); output.flush(); output.close(); } public long getProcessInstanceId() { return processInstanceId; } public void addScriptExecution(ScriptExecution execution) { scriptExecutions.add(execution); } public ScriptExecution getScriptExecution(int workItemId) { for (ScriptExecution execution : scriptExecutions) { if (execution.getWorkItemId() == workItemId) { return execution; } } return null; } public List<ScriptExecution> getScriptExecutions() { return scriptExecutions; } public enum Status { STARTED, COMPLETED, ABORTED } private Status status; public Status getStatus() { return status; } public boolean isCompleted() { return status == Status.COMPLETED; } public boolean isAborted() { return status == Status.ABORTED; } public boolean isRunning() { return status == Status.STARTED; } @Override public BallColor getIconColor() { if (status == Status.STARTED) { return BallColor.BLUE_ANIME; } else if (status == Status.ABORTED) { return BallColor.GREY; } else { return BallColor.BLUE; } } public synchronized void markCompleted() { setStatus(Status.COMPLETED); } public synchronized void markAborted() { setStatus(Status.ABORTED); } private synchronized void setStatus(Status status) { this.status = status; PrintWriter logWriter = getLogWriter(); try { save(); } catch (IOException e) { e.printStackTrace(logWriter); } logWriter.close(); } public void addLog(NodeInstanceLog log) { logs.add(log); try { save(); } catch (IOException e) { e.printStackTrace(); } } public synchronized PrintWriter getLogWriter() { if (logWriter == null) { try { logWriter = new PrintWriter(new FileWriter(getLogFile(), true), true); } catch (IOException e) { throw new RuntimeException( "Error opening log file for reading", e); } } return logWriter; } public HttpResponse doDoCancel() throws ServletException, IOException { checkPermission(Job.BUILD); getLogWriter().println("Workflow canceled by " + Hudson.getAuthentication().getName()); try { cancel(); } catch (Exception e) { throw new ServletException( "Error while canceling process instance #" + processInstanceId, e); } // TODO check if we get this through events already ? setStatus(Status.ABORTED); return new HttpRedirect(Hudson.getInstance().getRootUrl() + getUrl()); } public void cancel() throws Exception { getParent().run(new CancelProcessCallable(processInstanceId)); } public void dispose() { if (logWriter != null) logWriter.close(); } }