<property name="session" ref="session" />
</bean>
+ <bean id="jcrResultListView" class="org.argeo.slc.client.ui.views.JcrResultListView"
+ scope="prototype">
+ <property name="session" ref="session" />
+ </bean>
+
<bean id="session" class="org.argeo.jcr.spring.ThreadBoundSession">
<property name="repository" ref="repository" />
</bean>
name="Results"
restorable="true">
</view>
+ <view
+ class="org.argeo.eclipse.spring.SpringExtensionFactory"
+ icon="icons/results.gif"
+ id="org.argeo.slc.client.ui.jcrResultListView"
+ name="Results"
+ restorable="true">
+ </view>
<view
allowMultiple="true"
class="org.argeo.eclipse.spring.SpringExtensionFactory"
// add the views to the corresponding place holder
left.addView("org.argeo.slc.client.ui.jcrExecutionModulesView");
- left.addView("org.argeo.slc.client.ui.resultListView");
+ left.addView("org.argeo.slc.client.ui.jcrResultListView");
// main.addView("org.argeo.slc.client.ui.processBuilderView");
// main.addPlaceholder("org.argeo.slc.client.ui.resultDetailView:UUID-*");
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
-/**
- * This class display the list of all processes that have run in the
- * corresponding agent. Currently, the local agent.
- */
+/** Displays processes. */
public class JcrProcessListView extends ViewPart {
public static final String ID = "org.argeo.slc.client.ui.jcrProcessListView";
--- /dev/null
+package org.argeo.slc.client.ui.views;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+import javax.jcr.observation.ObservationManager;
+import javax.jcr.query.Query;
+
+import org.argeo.eclipse.ui.jcr.AsyncUiEventListener;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.slc.SlcException;
+import org.argeo.slc.client.ui.editors.ProcessEditor;
+import org.argeo.slc.client.ui.editors.ProcessEditorInput;
+import org.argeo.slc.jcr.SlcJcrConstants;
+import org.argeo.slc.jcr.SlcNames;
+import org.argeo.slc.jcr.SlcTypes;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.ViewPart;
+
+/** Displays results. */
+public class JcrResultListView extends ViewPart implements SlcNames {
+ public static final String ID = "org.argeo.slc.client.ui.jcrResultListView";
+
+ private TableViewer viewer;
+
+ private Session session;
+
+ private EventListener resultsObserver;
+
+ 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);
+ viewer.setLabelProvider(new LabelProvider());
+ viewer.setContentProvider(new ContentProvider());
+ viewer.setInput(getViewSite());
+ viewer.addDoubleClickListener(new ViewDoubleClickListener());
+
+ resultsObserver = new AsyncUiEventListener() {
+ protected void onEventInUiThread(EventIterator events) {
+ // TODO optimize by updating only the changed result
+ viewer.refresh();
+ }
+ };
+ try {
+ ObservationManager observationManager = session.getWorkspace()
+ .getObservationManager();
+ observationManager.addEventListener(resultsObserver,
+ Event.NODE_ADDED | Event.NODE_REMOVED
+ | Event.PROPERTY_CHANGED,
+ SlcJcrConstants.RESULTS_BASE_PATH, true, null, null,
+ false);
+ } catch (RepositoryException e) {
+ throw new SlcException("Cannot register listeners", e);
+ }
+
+ }
+
+ protected Table createTable(Composite parent) {
+ int style = SWT.SINGLE | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL
+ | SWT.FULL_SELECTION;
+ // does not function with RAP, commented for the time being
+ // | SWT.HIDE_SELECTION;
+
+ Table table = new Table(parent, style);
+
+ table.setLinesVisible(true);
+ table.setHeaderVisible(true);
+
+ TableColumn column = new TableColumn(table, SWT.LEFT, 0);
+ column.setText("Date");
+ column.setWidth(200);
+
+ column = new TableColumn(table, SWT.LEFT, 1);
+ column.setText("Id");
+ column.setWidth(300);
+
+ return table;
+ }
+
+ public void setFocus() {
+ viewer.getControl().setFocus();
+ }
+
+ @Override
+ public void dispose() {
+ JcrUtils.unregisterQuietly(session.getWorkspace(), resultsObserver);
+ super.dispose();
+ }
+
+ class ContentProvider implements IStructuredContentProvider {
+
+ public Object[] getElements(Object inputElement) {
+ try {
+ // TODO filter, optimize with virtual table, ...
+ String sql = "SELECT * from [slc:result] ORDER BY [jcr:lastModified] DESC";
+ Query query = session.getWorkspace().getQueryManager()
+ .createQuery(sql, Query.JCR_SQL2);
+ // TODO paging
+ query.setLimit(queryLimit);
+ List<Node> nodes = new ArrayList<Node>();
+ for (NodeIterator nit = query.execute().getNodes(); nit
+ .hasNext();) {
+ nodes.add(nit.nextNode());
+ }
+ return nodes.toArray();
+ } catch (RepositoryException e) {
+ throw new SlcException("Cannot retrieve processes", e);
+ }
+ }
+
+ public void dispose() {
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+
+ }
+
+ class LabelProvider extends ColumnLabelProvider implements
+ ITableLabelProvider {
+
+ public Image getColumnImage(Object obj, int columnIndex) {
+ if (columnIndex != 0)
+ return null;
+ try {
+ Node node = (Node) obj;
+ if(node.hasProperty(SLC_COMPLETED)){
+ // TODO
+ }
+ return null;
+ } catch (RepositoryException e) {
+ throw new SlcException("Cannot get column text", e);
+ }
+ }
+
+ public String getColumnText(Object obj, int index) {
+ try {
+ Node node = (Node) obj;
+ switch (index) {
+
+ case 0:
+ return dateFormat.format(node
+ .getProperty(Property.JCR_LAST_MODIFIED).getDate()
+ .getTime());
+ case 1:
+ return node.getProperty(SlcNames.SLC_UUID).getString();
+ }
+ return getText(obj);
+ } catch (RepositoryException e) {
+ throw new SlcException("Cannot get column text", e);
+ }
+ }
+
+ }
+
+ class ViewDoubleClickListener implements IDoubleClickListener {
+ public void doubleClick(DoubleClickEvent evt) {
+ Object obj = ((IStructuredSelection) evt.getSelection())
+ .getFirstElement();
+ try {
+ if (obj instanceof Node) {
+ Node node = (Node) obj;
+ if (node.isNodeType(SlcTypes.SLC_PROCESS)) {
+ IWorkbenchPage activePage = PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getActivePage();
+ activePage.openEditor(
+ new ProcessEditorInput(node.getPath()),
+ ProcessEditor.ID);
+ }
+ }
+ } catch (Exception e) {
+ throw new SlcException("Cannot open " + obj, e);
+ }
+ }
+
+ }
+
+ public void setSession(Session session) {
+ this.session = session;
+ }
+
+}
\ No newline at end of file
org.argeo.security,
org.argeo.security.core,
org.argeo.security.jcr,
+ org.argeo.slc.core.test.tree,
org.argeo.slc.execution,
org.argeo.slc.jcr,
org.argeo.slc.jcr.execution,
interface="org.springframework.security.AuthenticationManager" />\r
\r
<!-- SERVICES -->\r
+ <!-- Deprecated -->\r
+ <service ref="resultListener"\r
+ interface="org.argeo.slc.core.test.tree.TreeTestResultListener" />\r
+ <service ref="resultListener" interface="org.argeo.slc.test.TestResultListener" />\r
+ \r
<service interface="org.argeo.slc.execution.ExecutionModulesListener"\r
ref="executionModulesListener" />\r
<service ref="agent" interface="org.argeo.slc.runtime.SlcAgentFactory">\r
<bean id="executionModulesListener" class="org.argeo.slc.jcr.execution.JcrExecutionModulesListener"\r
init-method="init" destroy-method="dispose">\r
<property name="agent" ref="agent" />\r
- <property name="session" ref="session2">\r
+ <property name="session">\r
+ <bean factory-bean="repository" factory-method="login" />\r
</property>\r
</bean>\r
\r
- <bean id="session2" factory-bean="repository" factory-method="login" />\r
+ <bean id="resultListener" class="org.argeo.slc.jcr.execution.JcrResultListener">\r
+ <property name="session" ref="session" />\r
+ </bean>\r
+\r
\r
<bean id="session" class="org.argeo.security.jcr.SecureThreadBoundSession">\r
<property name="repository" ref="repository" />\r
public final static String PROPERTY_PATH = "argeo.slc.jcr.path";
public final static String PROCESSES_BASE_PATH = "/slc/processes";
public final static String AGENTS_BASE_PATH = "/slc/agents";
+ public final static String RESULTS_BASE_PATH = "/slc/results";
public final static String VM_AGENT_FACTORY_PATH = AGENTS_BASE_PATH + "/vm";
}
Calendar now = new GregorianCalendar();
return SlcJcrConstants.PROCESSES_BASE_PATH + '/'
+ JcrUtils.dateAsPath(now, true) + uuid;
+ }
+ /** Create a new execution result path based on the current time */
+ public static String createResultPath(String uuid) {
+ Calendar now = new GregorianCalendar();
+ return SlcJcrConstants.RESULTS_BASE_PATH + '/'
+ + JcrUtils.dateAsPath(now, true) + uuid;
}
/**
public final static String SLC_VALUE = "slc:value";
public final static String SLC_ADDRESS = "slc:address";
+ public final static String SLC_STARTED = "slc:started";
+ public final static String SLC_COMPLETED = "slc:completed";
+
public final static String SLC_SPEC = "slc:spec";
public final static String SLC_EXECUTION_SPECS = "slc:executionSpecs";
public final static String SLC_FLOW = "slc:flow";
+ // spec attribute
public final static String SLC_IS_IMMUTABLE = "slc:isImmutable";
public final static String SLC_IS_CONSTANT = "slc:isConstant";
public final static String SLC_IS_HIDDEN = "slc:isHidden";
+ // result
+ public final static String SLC_SUCCESS = "slc:success";
+ public final static String SLC_MESSAGE = "slc:message";
+ public final static String SLC_ERROR_MESSAGE = "slc:errorMessage";
+
}
public final static String SLC_PRIMITIVE_SPEC_ATTRIBUTE = "slc:primitiveSpecAttribute";
public final static String SLC_REF_SPEC_ATTRIBUTE = "slc:refSpecAttribute";
+ public final static String SLC_RESULT = "slc:result";
+ public final static String SLC_CHECK = "slc:check";
+
}
--- /dev/null
+package org.argeo.slc.jcr.execution;
+
+import java.util.Collections;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.query.Query;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.slc.core.attachment.Attachment;
+import org.argeo.slc.core.structure.tree.TreeSPath;
+import org.argeo.slc.core.test.tree.TreeTestResult;
+import org.argeo.slc.core.test.tree.TreeTestResultListener;
+import org.argeo.slc.jcr.SlcJcrUtils;
+import org.argeo.slc.jcr.SlcNames;
+import org.argeo.slc.jcr.SlcTypes;
+import org.argeo.slc.test.TestResultPart;
+import org.argeo.slc.test.TestStatus;
+
+/** Persists results in JCR */
+public class JcrResultListener implements TreeTestResultListener, SlcNames {
+ private final static Log log = LogFactory.getLog(JcrResultListener.class);
+
+ private Session session;
+
+ /** Caches the mapping between SLC uuids and internal JCR identifiers */
+ private Map<String, String> uuidToIdentifier = Collections
+ .synchronizedMap(new HashMap<String, String>());
+
+ public void resultPartAdded(TreeTestResult testResult,
+ TestResultPart testResultPart) {
+ try {
+ String uuid = testResult.getUuid();
+ Node resultNode = getResultNode(uuid);
+ if (resultNode == null) {
+ resultNode = createResultNode(testResult);
+ // session.save();
+ }
+ String partParentPath;
+ TreeSPath currentPath = testResult.getCurrentPath();
+ if (currentPath != null) {
+ String subPath = currentPath.getAsUniqueString();
+ partParentPath = resultNode.getPath() + subPath;
+ } else {
+ partParentPath = resultNode.getPath();
+ // TODO create some depth?
+ }
+
+ Node partParentNode;
+ if (session.itemExists(partParentPath)) {
+ partParentNode = session.getNode(partParentPath);
+ } else {
+ partParentNode = JcrUtils.mkdirs(session, partParentPath);
+ // session.save();
+ }
+ // create part node
+ // TODO find a better name
+ String partNodeName = Long.toString(System.currentTimeMillis());
+ Node resultPartNode = partParentNode.addNode(partNodeName,
+ SlcTypes.SLC_CHECK);
+ resultPartNode.setProperty(SLC_SUCCESS,
+ testResultPart.getStatus() == TestStatus.PASSED);
+ if (testResultPart.getMessage() != null)
+ resultPartNode.setProperty(SLC_MESSAGE,
+ testResultPart.getMessage());
+ if (testResultPart.getExceptionMessage() != null)
+ resultPartNode.setProperty(SLC_ERROR_MESSAGE,
+ testResultPart.getExceptionMessage());
+ // JcrUtils.debug(resultPartNode);
+
+ JcrUtils.updateLastModified(resultNode);
+
+ session.save();
+ } catch (RepositoryException e) {
+ JcrUtils.discardQuietly(session);
+ log.error("Cannot add result part " + testResultPart + " to "
+ + testResult, e);
+ // throw new SlcException("Cannot add result part " + testResultPart
+ // + " to " + testResult, e);
+ }
+
+ }
+
+ /** @return null if does not exist */
+ protected Node getResultNode(String uuid) throws RepositoryException {
+ Node resultNode;
+ if (uuidToIdentifier.containsKey(uuid)) {
+ return session.getNodeByIdentifier(uuidToIdentifier.get(uuid));
+ } else {
+ Query q = session
+ .getWorkspace()
+ .getQueryManager()
+ .createQuery(
+ "select * from [slc:result] where [slc:uuid]='"
+ + uuid + "'", Query.JCR_SQL2);
+ resultNode = JcrUtils.querySingleNode(q);
+ if (resultNode != null)
+ uuidToIdentifier.put(uuid, resultNode.getIdentifier());
+ }
+ return resultNode;
+ }
+
+ protected Node createResultNode(TreeTestResult testResult)
+ throws RepositoryException {
+ String uuid = testResult.getUuid();
+ String path = SlcJcrUtils.createResultPath(uuid);
+ Node resultNode = JcrUtils.mkdirs(session, path, SlcTypes.SLC_RESULT);
+ resultNode.setProperty(SLC_UUID, uuid);
+ for (Map.Entry<String, String> entry : testResult.getAttributes()
+ .entrySet()) {
+ resultNode.setProperty(entry.getKey(), entry.getValue());
+ }
+
+ uuidToIdentifier.put(uuid, resultNode.getIdentifier());
+ return resultNode;
+ }
+
+ public void close(TreeTestResult testResult) {
+ try {
+ String uuid = testResult.getUuid();
+ Node resultNode = getResultNode(uuid);
+ if (resultNode == null)
+ resultNode = createResultNode(testResult);
+ JcrUtils.updateLastModified(resultNode);
+ GregorianCalendar closeDate = new GregorianCalendar();
+ closeDate.setTime(testResult.getCloseDate());
+ resultNode.setProperty(SLC_COMPLETED, closeDate);
+
+ uuidToIdentifier.remove(uuid);
+ session.save();
+ } catch (RepositoryException e) {
+ JcrUtils.discardQuietly(session);
+ log.error("Cannot close result " + testResult, e);
+ // throw new SlcException("Cannot close result " + testResult, e);
+ }
+
+ }
+
+ public void addAttachment(TreeTestResult testResult, Attachment attachment) {
+
+ }
+
+ public void setSession(Session session) {
+ this.session = session;
+ }
+
+}
+ * (mix:title)
[slc:executionFlow] > nt:unstructured, mix:title
-- slc:name (STRING) m
+- slc:name (STRING) ! m
// if the execution spec is a referenceable node
- slc:spec (REFERENCE)
// if the execution spec is internal (without name)
// PROCESS
[slc:process] > nt:unstructured, mix:created, mix:lastModified
-- slc:uuid (STRING) m
+- slc:uuid (STRING) ! m
- slc:status (STRING) m
+ slc:flow (slc:realizedFlow)
// the realized execution spec attributes
+ * (slc:executionSpecAttribute) *
+// RESULT
+[slc:result] > nt:unstructured, mix:created, mix:lastModified
+- slc:uuid (STRING) ! m
+- slc:completed (DATE)
+
+[slc:check] > nt:unstructured, mix:created
+// true for PASSED, false for FAILED or ERROR
+- slc:success (BOOLEAN) ! m
+- slc:message (STRING)
+// ERROR if set, the check could not be performed because of an unexpected exception
+- slc:errorMessage (STRING)