]> git.argeo.org Git - gpl/argeo-slc.git/commitdiff
Manages results in JCR
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 28 Apr 2011 14:44:10 +0000 (14:44 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 28 Apr 2011 14:44:10 +0000 (14:44 +0000)
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@4487 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

14 files changed:
eclipse/plugins/org.argeo.slc.client.ui/META-INF/spring/jcr.xml
eclipse/plugins/org.argeo.slc.client.ui/plugin.xml
eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/SlcExecutionPerspective.java
eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/views/JcrProcessListView.java
eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/views/JcrResultListView.java [new file with mode: 0644]
modules/agent/org.argeo.slc.agent.jcr/META-INF/MANIFEST.MF
modules/agent/org.argeo.slc.agent.jcr/META-INF/spring/jcr-osgi.xml
modules/agent/org.argeo.slc.agent.jcr/META-INF/spring/jcr.xml
runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/SlcJcrConstants.java
runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/SlcJcrUtils.java
runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/SlcNames.java
runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/SlcTypes.java
runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/execution/JcrResultListener.java [new file with mode: 0644]
runtime/org.argeo.slc.support.jcr/src/main/resources/org/argeo/slc/jcr/slc.cnd

index 340ae1c780fbd8ce62f0d16f04071bf4eeb44d50..dccaea56f0bdd06ef9d96368817da13c6ac89606 100644 (file)
                <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>
index 96f9fe1f95814a544f9ea009caaf938a8849f124..0b7d2f0120b337cf9105da12301ace3912c61956 100644 (file)
                 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"
index 144131fe0d79a709d1e06df2fc41197bed0a8160..d5ba0b0d81c4d67437659f5361c84f9b8b63846b 100644 (file)
@@ -31,7 +31,7 @@ public class SlcExecutionPerspective implements IPerspectiveFactory {
 
                // 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-*");
index 2df53034bb347c24d8cb60bc86f5dcd3e4dc1935..cfc57497954490348ebcdac1a4eb88cfa98a1907 100644 (file)
@@ -43,10 +43,7 @@ import org.eclipse.ui.IWorkbenchPage;
 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";
 
diff --git a/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/views/JcrResultListView.java b/eclipse/plugins/org.argeo.slc.client.ui/src/main/java/org/argeo/slc/client/ui/views/JcrResultListView.java
new file mode 100644 (file)
index 0000000..a886e05
--- /dev/null
@@ -0,0 +1,210 @@
+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
index 15f683298f798557ae1bc26ddd8a6da344c459a4..19a0f4f8fbb4b74f2100175c7fd9c0cff9cf1ad1 100644 (file)
@@ -7,6 +7,7 @@ Import-Package: javax.jcr,
  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,
index a6f779f378959f6a2d9202ace91c7f74917c821b..525c1643768539246ec55102ed182aacfaf8699b 100644 (file)
                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
index c07f293e68a4552523a9ad1d3dfcced0d5b8429e..6c1f43c42803bc253bf18778b9dcd037e9880215 100644 (file)
        <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
index eb1cf46e057c3c1191c0bb62a926bc3fbc763189..3775afadf8879355491c057fc1511dedd67de9e0 100644 (file)
@@ -4,5 +4,6 @@ public interface SlcJcrConstants {
        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";
 }
index fcd850aaa9ae76e991e82ade625faf9e5183e26d..c4a14ff2926284bb98191397ffcf40f8e1770bdf 100644 (file)
@@ -64,7 +64,13 @@ public class SlcJcrUtils {
                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;
        }
 
        /**
index fa270415083ce50e6bfe5c8cba65ecc8b00ecd49..d41f80cf0537846902369de86833dc6b2e5fa207 100644 (file)
@@ -11,12 +11,21 @@ public interface SlcNames {
        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";
+
 }
index 6ac10e3b014dfd9db213f998d2dfd2810a2d913c..9afbb73c23da357c8a6d36158b8b07f95071ba1a 100644 (file)
@@ -16,4 +16,7 @@ public interface SlcTypes {
        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";
+
 }
diff --git a/runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/execution/JcrResultListener.java b/runtime/org.argeo.slc.support.jcr/src/main/java/org/argeo/slc/jcr/execution/JcrResultListener.java
new file mode 100644 (file)
index 0000000..8cd72ef
--- /dev/null
@@ -0,0 +1,154 @@
+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;
+       }
+
+}
index 126b16f720926306c3be1328b07688e330c41aa5..cb88e55046e6ed5ccf9f2b9a5811d2341ab967bf 100644 (file)
@@ -45,7 +45,7 @@ mixin
 + * (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)
@@ -53,7 +53,7 @@ mixin
 
 // 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)
 
@@ -70,4 +70,15 @@ mixin
 // 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)