From: Mathieu Baudier Date: Wed, 20 Apr 2011 07:57:21 +0000 (+0000) Subject: Improve process management X-Git-Tag: argeo-slc-2.1.7~971 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=9bab32638cc6a08ba96c3feca190733e9d1ac0ac;p=gpl%2Fargeo-slc.git Improve process management ASSIGNED - bug 17: Generalize agent management and registration beyond JMS https://bugzilla.argeo.org/show_bug.cgi?id=17 git-svn-id: https://svn.argeo.org/slc/trunk@4462 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/demo/slc_demo_rcp.properties b/demo/slc_demo_rcp.properties index f90dd780f..2e380ad92 100644 --- a/demo/slc_demo_rcp.properties +++ b/demo/slc_demo_rcp.properties @@ -1,7 +1,7 @@ argeo.osgi.start=\ org.springframework.osgi.extender,\ org.argeo.slc.client.rcp,\ -org.argeo.slc.server.jcr,\ +org.argeo.slc.agent.jcr,\ org.argeo.slc.demo.ant,\ org.argeo.slc.demo.basic,\ diff --git a/eclipse/plugins/org.argeo.slc.client.ui/icons/kill.png b/eclipse/plugins/org.argeo.slc.client.ui/icons/kill.png new file mode 100644 index 000000000..cfbf9d15e Binary files /dev/null and b/eclipse/plugins/org.argeo.slc.client.ui/icons/kill.png differ diff --git a/eclipse/plugins/org.argeo.slc.client.ui/icons/relaunch.gif b/eclipse/plugins/org.argeo.slc.client.ui/icons/relaunch.gif new file mode 100644 index 000000000..8f943a8c3 Binary files /dev/null and b/eclipse/plugins/org.argeo.slc.client.ui/icons/relaunch.gif differ diff --git a/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/SlcImages.java b/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/SlcImages.java index 1bc7ff512..d9fe3cddb 100644 --- a/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/SlcImages.java +++ b/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/SlcImages.java @@ -13,6 +13,7 @@ public class SlcImages { public final static Image PASSED = img("icons/passed.gif"); public final static Image ERROR = img("icons/error.gif"); public final static Image LAUNCH = img("icons/launch.gif"); + public final static Image RELAUNCH = img("icons/relaunch.gif"); public final static Image REMOVE_ONE = img("icons/remove_one.gif"); public final static Image REMOVE_ALL = img("icons/removeAll.png"); } diff --git a/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/editors/ProcessBuilderPage.java b/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/editors/ProcessBuilderPage.java index 6a2178089..9154714d8 100644 --- a/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/editors/ProcessBuilderPage.java +++ b/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/editors/ProcessBuilderPage.java @@ -3,11 +3,13 @@ package org.argeo.slc.client.ui.editors; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.UUID; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.Property; import javax.jcr.RepositoryException; +import javax.jcr.Session; import javax.jcr.nodetype.NodeType; import javax.jcr.observation.Event; import javax.jcr.observation.EventIterator; @@ -15,6 +17,7 @@ import javax.jcr.observation.EventListener; import javax.jcr.observation.ObservationManager; import org.argeo.ArgeoException; +import org.argeo.eclipse.ui.jcr.AsyncUiEventListener; import org.argeo.jcr.JcrUtils; import org.argeo.slc.SlcException; import org.argeo.slc.client.ui.SlcImages; @@ -44,6 +47,7 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowData; import org.eclipse.swt.layout.RowLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; @@ -57,20 +61,23 @@ import org.eclipse.ui.forms.widgets.ScrolledForm; public class ProcessBuilderPage extends FormPage implements SlcNames, SlcTypes { public final static String ID = "processBuilderPage"; - //private final static Log log = LogFactory.getLog(ProcessBuilderPage.class); + // private final static Log log = + // LogFactory.getLog(ProcessBuilderPage.class); private Node processNode; private TreeViewer flowsViewer; - private Label status; + private Label statusLabel; + private Button run; + private Button remove; + private Button clear; private AbstractFormPart formPart; - private StatusObserver statusObserver; + private EventListener statusObserver; public ProcessBuilderPage(ProcessEditor editor, Node processNode) { super(editor, ID, "Definition"); this.processNode = processNode; - } @Override @@ -89,11 +96,13 @@ public class ProcessBuilderPage extends FormPage implements SlcNames, SlcTypes { }; getManagedForm().addPart(formPart); - if (getProcessStatus().equals(ExecutionProcess.UNINITIALIZED)) - formPart.markDirty(); // observation - statusObserver = new StatusObserver(); + statusObserver = new AsyncUiEventListener() { + protected void onEventInUiThread(EventIterator events) { + statusChanged(); + } + }; ObservationManager observationManager = processNode.getSession() .getWorkspace().getObservationManager(); observationManager.addEventListener(statusObserver, @@ -112,12 +121,16 @@ public class ProcessBuilderPage extends FormPage implements SlcNames, SlcTypes { controls.setLayout(new RowLayout()); controls.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - Button run = tk.createButton(controls, null, SWT.PUSH); + run = tk.createButton(controls, null, SWT.PUSH); run.setToolTipText("Run"); run.setImage(SlcImages.LAUNCH); run.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { - ((ProcessEditor) getEditor()).process(); + if (isFinished(getProcessStatus())) { + relaunch(); + } else { + ((ProcessEditor) getEditor()).process(); + } } public void widgetDefaultSelected(SelectionEvent e) { @@ -125,7 +138,7 @@ public class ProcessBuilderPage extends FormPage implements SlcNames, SlcTypes { } }); - Button remove = tk.createButton(controls, null, SWT.PUSH); + remove = tk.createButton(controls, null, SWT.PUSH); remove.setImage(SlcImages.REMOVE_ONE); remove.setToolTipText("Remove selected flows"); remove.addSelectionListener(new SelectionListener() { @@ -138,7 +151,7 @@ public class ProcessBuilderPage extends FormPage implements SlcNames, SlcTypes { } }); - Button clear = tk.createButton(controls, null, SWT.PUSH); + clear = tk.createButton(controls, null, SWT.PUSH); clear.setImage(SlcImages.REMOVE_ALL); clear.setToolTipText("Clear all flows"); clear.addSelectionListener(new SelectionListener() { @@ -151,7 +164,53 @@ public class ProcessBuilderPage extends FormPage implements SlcNames, SlcTypes { } }); - status = tk.createLabel(controls, getProcessStatus()); + Composite statusComposite = tk.createComposite(controls); + RowData rowData = new RowData(); + rowData.width = 100; + rowData.height = 16; + statusComposite.setLayoutData(rowData); + statusComposite.setLayout(new FillLayout()); + statusLabel = tk.createLabel(statusComposite, getProcessStatus()); + + // make sure all controls are in line with status + statusChanged(); + } + + protected void relaunch() { + try { + Node duplicatedNode = duplicateProcess(); + PlatformUI + .getWorkbench() + .getActiveWorkbenchWindow() + .getActivePage() + .openEditor( + new ProcessEditorInput(duplicatedNode.getPath()), + ProcessEditor.ID); + getEditor().close(false); + } catch (Exception e1) { + throw new SlcException("Cannot relaunch " + processNode, e1); + } + } + + protected Node duplicateProcess() { + try { + Session session = processNode.getSession(); + String uuid = UUID.randomUUID().toString(); + String destPath = SlcJcrUtils.createExecutionProcessPath(uuid); + Node newNode = JcrUtils.mkdirs(session, destPath, SLC_PROCESS); + JcrUtils.copy(processNode, newNode); +// session.getWorkspace().copy(processNode.getPath(), destPath); +// Node newNode = session.getNode(destPath); + // make sure that we kept the mixins +// newNode.addMixin(NodeType.MIX_CREATED); +// newNode.addMixin(NodeType.MIX_LAST_MODIFIED); + newNode.setProperty(SLC_UUID, uuid); + newNode.setProperty(SLC_STATUS, ExecutionProcess.INITIALIZED); + session.save(); + return newNode; + } catch (RepositoryException e) { + throw new SlcException("Cannot duplicate process", e); + } } protected String getProcessStatus() { @@ -163,6 +222,33 @@ public class ProcessBuilderPage extends FormPage implements SlcNames, SlcTypes { } } + protected void statusChanged() { + String status = getProcessStatus(); + statusLabel.setText(status); + Boolean isEditable = isEditable(status); + run.setEnabled(isEditable); + remove.setEnabled(isEditable); + clear.setEnabled(isEditable); + // flowsViewer.getTree().setEnabled(isEditable); + if (status.equals(ExecutionProcess.COMPLETED) + || status.equals(ExecutionProcess.ERROR)) { + run.setEnabled(true); + run.setImage(SlcImages.RELAUNCH); + run.setToolTipText("Relaunch"); + } + } + + /** Optimization so that we don't call the node each time */ + protected Boolean isEditable(String status) { + return status.equals(ExecutionProcess.NEW) + || status.equals(ExecutionProcess.INITIALIZED); + } + + protected Boolean isFinished(String status) { + return status.equals(ExecutionProcess.COMPLETED) + || status.equals(ExecutionProcess.ERROR); + } + protected void createBuilder(Composite parent) { FormToolkit tk = getManagedForm().getToolkit(); SashForm sashForm = new SashForm(parent, SWT.HORIZONTAL); @@ -247,14 +333,11 @@ public class ProcessBuilderPage extends FormPage implements SlcNames, SlcTypes { } public void commit(Boolean onSave) { + if (onSave) + statusLabel.setText(getProcessStatus()); formPart.commit(onSave); } - @Override - public void setFocus() { - flowsViewer.getTree().setFocus(); - } - @Override public void dispose() { JcrUtils.unregisterQuietly(processNode, statusObserver); @@ -367,20 +450,7 @@ public class ProcessBuilderPage extends FormPage implements SlcNames, SlcTypes { @Override public boolean validateDrop(Object target, int operation, TransferData transferType) { - return true; + return isEditable(getProcessStatus()); } } - - class StatusObserver implements EventListener { - - public void onEvent(EventIterator events) { - PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { - public void run() { - status.setText(getProcessStatus()); - } - }); - // flowsViewer.refresh(); - } - - } } diff --git a/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/editors/ProcessEditor.java b/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/editors/ProcessEditor.java index 4da6f061a..e898a64a1 100644 --- a/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/editors/ProcessEditor.java +++ b/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/editors/ProcessEditor.java @@ -1,7 +1,5 @@ package org.argeo.slc.client.ui.editors; -import java.util.Calendar; -import java.util.GregorianCalendar; import java.util.UUID; import javax.jcr.Node; @@ -15,7 +13,7 @@ import org.argeo.slc.SlcException; import org.argeo.slc.client.ui.ClientUiPlugin; import org.argeo.slc.client.ui.controllers.ProcessController; import org.argeo.slc.execution.ExecutionProcess; -import org.argeo.slc.jcr.SlcJcrConstants; +import org.argeo.slc.jcr.SlcJcrUtils; import org.argeo.slc.jcr.SlcNames; import org.argeo.slc.jcr.SlcTypes; import org.eclipse.core.runtime.IProgressMonitor; @@ -33,7 +31,7 @@ public class ProcessEditor extends FormEditor implements SlcTypes, SlcNames { private ProcessBuilderPage builderPage; private ProcessLogPage logPage; - + @Override public void init(IEditorSite site, IEditorInput input) throws PartInitException { @@ -58,13 +56,11 @@ public class ProcessEditor extends FormEditor implements SlcTypes, SlcNames { protected Node newProcessNode(ProcessEditorInput pei) throws RepositoryException { - Calendar now = new GregorianCalendar(); String uuid = UUID.randomUUID().toString(); - String processPath = SlcJcrConstants.PROCESSES_BASE_PATH + '/' - + JcrUtils.dateAsPath(now, true) + uuid; + String processPath = SlcJcrUtils.createExecutionProcessPath(uuid); Node processNode = JcrUtils.mkdirs(session, processPath, SLC_PROCESS); processNode.setProperty(SLC_UUID, uuid); - processNode.setProperty(SLC_STATUS, ExecutionProcess.UNINITIALIZED); + processNode.setProperty(SLC_STATUS, ExecutionProcess.NEW); Node processFlow = processNode.addNode(SLC_FLOW); processFlow.addMixin(SLC_REALIZED_FLOW); @@ -79,6 +75,22 @@ public class ProcessEditor extends FormEditor implements SlcTypes, SlcNames { return processNode; } + @Override + public boolean isDirty() { + if (getProcessStatus().equals(ExecutionProcess.NEW)) + return true; + return super.isDirty(); + } + + protected String getProcessStatus() { + try { + return processNode.getProperty(SLC_STATUS).getString(); + } catch (RepositoryException e) { + throw new SlcException("Cannot retrieve status for " + processNode, + e); + } + } + @Override public void dispose() { JcrUtils.logoutQuietly(session); @@ -114,12 +126,12 @@ public class ProcessEditor extends FormEditor implements SlcTypes, SlcNames { public void doSave(IProgressMonitor monitor) { try { String status = processNode.getProperty(SLC_STATUS).getString(); - if (status.equals(ExecutionProcess.UNINITIALIZED)) + if (status.equals(ExecutionProcess.NEW)) processNode.setProperty(SLC_STATUS, ExecutionProcess.INITIALIZED); session.save(); builderPage.commit(true); - firePropertyChange(PROP_DIRTY); + editorDirtyStateChanged(); } catch (RepositoryException e) { throw new SlcException("Cannot save " + processNode, e); } finally { diff --git a/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/editors/ProcessEditorInput.java b/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/editors/ProcessEditorInput.java index 9a94566da..f93af3fed 100644 --- a/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/editors/ProcessEditorInput.java +++ b/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/editors/ProcessEditorInput.java @@ -74,4 +74,14 @@ public class ProcessEditorInput implements IEditorInput, IPersistableElement { return launchImmediately; } + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ProcessEditorInput)) + return false; + ProcessEditorInput pei = (ProcessEditorInput) obj; + if (processPath != null && pei.processPath != null) + return processPath.equals(pei.processPath); + return false; + } + } diff --git a/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/views/JcrProcessListView.java b/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/views/JcrProcessListView.java index 8c2bb4007..ddc34a581 100644 --- a/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/views/JcrProcessListView.java +++ b/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/views/JcrProcessListView.java @@ -1,5 +1,7 @@ package org.argeo.slc.client.ui.views; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; @@ -52,6 +54,10 @@ public class JcrProcessListView extends ViewPart { private EventListener processesObserver; + private DateFormat dateFormat = new SimpleDateFormat( + "EEE, dd MMM yyyy HH:mm:ss"); + private Integer queryLimit = 100; + public void createPartControl(Composite parent) { Table table = createTable(parent); viewer = new TableViewer(table); @@ -127,6 +133,8 @@ public class JcrProcessListView extends ViewPart { String sql = "SELECT * from [slc:process] ORDER BY [jcr:lastModified] DESC"; Query query = session.getWorkspace().getQueryManager() .createQuery(sql, Query.JCR_SQL2); + // TODO paging + query.setLimit(queryLimit); List nodes = new ArrayList(); for (NodeIterator nit = query.execute().getNodes(); nit .hasNext();) { @@ -150,7 +158,6 @@ public class JcrProcessListView extends ViewPart { ITableLabelProvider { public Image getColumnImage(Object element, int columnIndex) { - // TODO Auto-generated method stub return null; } @@ -160,7 +167,9 @@ public class JcrProcessListView extends ViewPart { switch (index) { case 0: - return node.getProperty(Property.JCR_CREATED).getString(); + return dateFormat.format(node + .getProperty(Property.JCR_LAST_MODIFIED).getDate() + .getTime()); case 1: return "local"; case 2: diff --git a/runtime/org.argeo.slc.specs/src/main/java/org/argeo/slc/execution/ExecutionProcess.java b/runtime/org.argeo.slc.specs/src/main/java/org/argeo/slc/execution/ExecutionProcess.java index 58393dd90..35a84fb7b 100644 --- a/runtime/org.argeo.slc.specs/src/main/java/org/argeo/slc/execution/ExecutionProcess.java +++ b/runtime/org.argeo.slc.specs/src/main/java/org/argeo/slc/execution/ExecutionProcess.java @@ -9,7 +9,7 @@ package org.argeo.slc.execution; */ public interface ExecutionProcess { /** The process is not yet usable. */ - public final static String UNINITIALIZED = "UNINITIALIZED"; + public final static String NEW = "NEW"; /** The process is usable but not yet scheduled to run. */ public final static String INITIALIZED = "INITIALIZED"; /** The process is usable and scheduled to run, but not yet running. */ diff --git a/runtime/org.argeo.slc.specs/src/main/java/org/argeo/slc/process/SlcExecution.java b/runtime/org.argeo.slc.specs/src/main/java/org/argeo/slc/process/SlcExecution.java index 37b647ad3..d11f99922 100644 --- a/runtime/org.argeo.slc.specs/src/main/java/org/argeo/slc/process/SlcExecution.java +++ b/runtime/org.argeo.slc.specs/src/main/java/org/argeo/slc/process/SlcExecution.java @@ -34,7 +34,7 @@ public class SlcExecution implements ExecutionProcess, Serializable { private String host; private String user; private String type; - private String status = UNINITIALIZED; + private String status = NEW; private Map attributes = new TreeMap(); /** TODO: Synchronize */ diff --git a/runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/SlcJcrUtils.java b/runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/SlcJcrUtils.java index 0cda5a86d..ed2294f30 100644 --- a/runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/SlcJcrUtils.java +++ b/runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/SlcJcrUtils.java @@ -1,5 +1,10 @@ package org.argeo.slc.jcr; +import java.util.Calendar; +import java.util.GregorianCalendar; + +import org.argeo.jcr.JcrUtils; + /** Utilities around the SLC JCR model. Note that it relies on fixed base paths. */ public class SlcJcrUtils { public final static Integer AGENT_FACTORY_DEPTH = 3; @@ -39,6 +44,14 @@ public class SlcJcrUtils { return buf.toString(); } + /** Create a new execution process path based on the current time */ + public static String createExecutionProcessPath(String uuid) { + Calendar now = new GregorianCalendar(); + return SlcJcrConstants.PROCESSES_BASE_PATH + '/' + + JcrUtils.dateAsPath(now, true) + uuid; + + } + /** Prevents instantiation */ private SlcJcrUtils() { diff --git a/runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/execution/JcrExecutionProcess.java b/runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/execution/JcrExecutionProcess.java index 998027e8e..ce08b8f38 100644 --- a/runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/execution/JcrExecutionProcess.java +++ b/runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/execution/JcrExecutionProcess.java @@ -35,6 +35,9 @@ public class JcrExecutionProcess implements ExecutionProcess { public void setStatus(String status) { try { node.setProperty(SlcNames.SLC_STATUS, status); + // last modified properties needs to be manually updated + // see https://issues.apache.org/jira/browse/JCR-2233 + JcrUtils.updateLastModified(node); node.getSession().save(); } catch (RepositoryException e) { try {