diff options
| author | Andrew Branson <andrew.branson@cern.ch> | 2012-07-12 14:52:36 +0200 |
|---|---|---|
| committer | Andrew Branson <andrew.branson@cern.ch> | 2012-07-12 14:52:36 +0200 |
| commit | 20c81748688547c1b13686f15c65fbb1d60d81a0 (patch) | |
| tree | 59dc2b94c47ed30b8bc93c07cab42e96904d1146 | |
| parent | 61559eef9369dbdbb027bec7c571d7f770b2e7a3 (diff) | |
Suspend transition now can carry an optional 'Errors' outcome where
client processes can record the reason for the suspension. Errors XML is
marshalled ErrorInfo. UserCodeProcess automatically sends fatal
ErrorInfos through suspend jobs.
Job API added to to support all this.
fixes #23
13 files changed, 190 insertions, 55 deletions
diff --git a/bin/eclipse/Cristal 2.3 UserCode.launch b/bin/eclipse/Cristal 2.3 UserCode.launch new file mode 100644 index 0000000..cc227eb --- /dev/null +++ b/bin/eclipse/Cristal 2.3 UserCode.launch @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<stringAttribute key="bad_container_name" value="\cristal-kernel\bin\eclip"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/cristal-kernel/src/main/java/com/c2kernel/process/UserCodeProcess.java"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.debug.ui.ATTR_CONSOLE_ENCODING" value="UTF-8"/>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.m2e.launchconfig.classpathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.c2kernel.process.UserCodeProcess"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-logLevel 0 -config bin\conf\client.conf"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="cristal-kernel"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.m2e.launchconfig.sourcepathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256m"/>
+</launchConfiguration>
diff --git a/src/main/java/com/c2kernel/entity/agent/Job.java b/src/main/java/com/c2kernel/entity/agent/Job.java index b5274ec..216a88d 100644 --- a/src/main/java/com/c2kernel/entity/agent/Job.java +++ b/src/main/java/com/c2kernel/entity/agent/Job.java @@ -11,6 +11,7 @@ import com.c2kernel.persistency.ClusterStorage; import com.c2kernel.persistency.outcome.Outcome;
import com.c2kernel.persistency.outcome.Viewpoint;
import com.c2kernel.process.Gateway;
+import com.c2kernel.scripting.ErrorInfo;
import com.c2kernel.utils.CastorHashMap;
import com.c2kernel.utils.KeyValuePair;
import com.c2kernel.utils.Logger;
@@ -46,12 +47,16 @@ public class Job implements C2KLocalObject private CastorHashMap mActProps = new CastorHashMap();
private String mOutcome;
+
+ private ErrorInfo mError;
private String mStepType;
private ItemProxy item = null;
private AgentProxy agent = null;
+
+ private boolean outcomeSet;
/***************************************************************************
* Empty constructor for Castor
@@ -105,29 +110,43 @@ public class Job implements C2KLocalObject public String getSchemaType()
{
- return (String) mActProps.get("SchemaType");
+ if (isError()) return "Errors";
+ if (requiresOutcome()) return (String)mActProps.get("SchemaType");
+ else return null;
}
public int getSchemaVersion()
{
- try
- {
- return Integer.parseInt((String) mActProps.get("SchemaVersion"));
- } catch (NumberFormatException ex)
- {
- return -1;
- }
+ if (isError()) return 0;
+ if (requiresOutcome())
+ try {
+ return Integer.parseInt((String) mActProps.get("SchemaVersion"));
+ } catch (NumberFormatException ex) {
+ return -1;
+ }
+ return -1;
}
public void setOutcome(String outcome)
{
- mOutcome = outcome;
+ mOutcome = outcome;
+ outcomeSet = !(mOutcome == null);
+ }
+
+ public void setError(ErrorInfo errors)
+ {
+ mError = errors;
+ try {
+ mOutcome = Gateway.getMarshaller().marshall(errors);
+ } catch (Exception e) {
+ Logger.error("Error marshalling ErrorInfo in job");
+ Logger.error(e);
+ }
}
public String getOutcomeString()
{
- Logger.debug(8, "getOutcomeString() " + (mOutcome == null && isOutcomeUsed()));
- if (mOutcome == null && isOutcomeUsed())
+ if (mOutcome == null && requiresOutcome())
{
String viewName = (String) getActProp("Viewpoint");
mOutcome = null;
@@ -136,16 +155,19 @@ public class Job implements C2KLocalObject {
Viewpoint view = (Viewpoint) Gateway.getStorage().get(getItemSysKey(), ClusterStorage.VIEWPOINT + "/" + getSchemaType() + "/" + viewName, null);
mOutcome = view.getOutcome().getData();
+ outcomeSet = true;
} catch (Exception ex)
- { // not found, return null
+ {
+ mOutcome = null;
+ outcomeSet = false;
}
+
}
return mOutcome;
}
public Outcome getOutcome()
{
- Logger.msg(1, "Get outcome");
return new Outcome(-1, getOutcomeString(), getSchemaType(), getSchemaVersion());
}
@@ -186,10 +208,22 @@ public class Job implements C2KLocalObject mAgentRole = role;
}
- public boolean isOutcomeUsed()
+ public boolean requiresOutcome()
{
- String schemaType = getSchemaType();
- return (Boolean.TRUE.equals(getActProp("AlwaysUseOutcome")) || mPossibleTransition == Transitions.DONE || mPossibleTransition == Transitions.COMPLETE) && !(schemaType == null || schemaType.equals(""));
+ String schemaType = (String) mActProps.get("SchemaVersion");
+ return (mPossibleTransition == Transitions.DONE || mPossibleTransition == Transitions.COMPLETE) && !(schemaType == null || schemaType.equals(""));
+ }
+
+ public boolean isError() {
+ return (mPossibleTransition == Transitions.SUSPEND);
+ }
+
+ public boolean hasOutcome() {
+ return requiresOutcome() || isError();
+ }
+
+ public boolean isOutcomeSet() {
+ return outcomeSet;
}
public int getID()
diff --git a/src/main/java/com/c2kernel/entity/proxy/AgentProxy.java b/src/main/java/com/c2kernel/entity/proxy/AgentProxy.java index 5c6a37e..286be88 100644 --- a/src/main/java/com/c2kernel/entity/proxy/AgentProxy.java +++ b/src/main/java/com/c2kernel/entity/proxy/AgentProxy.java @@ -36,6 +36,7 @@ import com.c2kernel.persistency.outcome.Schema; import com.c2kernel.process.Gateway;
import com.c2kernel.scripting.ErrorInfo;
import com.c2kernel.scripting.Script;
+import com.c2kernel.scripting.ScriptErrorException;
import com.c2kernel.scripting.ScriptingEngineException;
import com.c2kernel.utils.LocalObjectLoader;
import com.c2kernel.utils.Logger;
@@ -98,6 +99,7 @@ public class AgentProxy extends EntityProxy *
* @param item - item holding this job
* @param job - the job to execute
+ * @throws ScriptErrorException
*/
public void execute(ItemProxy item, Job job)
throws AccessRightsException,
@@ -105,24 +107,19 @@ public class AgentProxy extends EntityProxy ObjectNotFoundException,
InvalidDataException,
PersistencyException,
- ObjectAlreadyExistsException
+ ObjectAlreadyExistsException,
+ ScriptErrorException
{
OutcomeValidator validator = null;
String scriptName = job.getActPropString("ScriptName");
Date startTime = new Date();
Logger.msg(3, "AgentProxy - executing "+job.getStepPath()+" for "+path.getAgentName());
// get the outcome validator if present
- if (job.isOutcomeUsed())
+ if (job.isOutcomeSet())
{
+ String schemaName = job.getSchemaType();
+ int schemaVersion = job.getSchemaVersion();
- // get schema info from act props
- String schemaName = job.getActPropString("SchemaType");
- int schemaVersion;
- try {
- schemaVersion = Integer.parseInt(job.getActPropString("SchemaVersion"));
- } catch (Exception e) {
- throw new InvalidDataException(e.getClass().getName()+" extracing schema version", "");
- }
Logger.msg(5, "AgentProxy - fetching schema "+schemaName+"_"+schemaVersion+" for validation");
// retrieve schema
Schema schema = LocalObjectLoader.getSchema(schemaName, schemaVersion);
@@ -155,20 +152,20 @@ public class AgentProxy extends EntityProxy // load script
ErrorInfo scriptErrors = (ErrorInfo)callScript(item, job);
+ String errorString = scriptErrors.toString();
if (scriptErrors.getFatal()) {
Logger.msg(3, "AgentProxy - fatal script error");
- Logger.error(scriptErrors.getErrors());
- throw new InvalidDataException("Fatal Script Error: \n"+scriptErrors.getErrors(), "");
+ throw new ScriptErrorException(scriptErrors);
}
- if (scriptErrors.getErrors().length() > 0)
- Logger.warning("Script errors: "+scriptErrors.getErrors());
+ if (errorString.length() > 0)
+ Logger.warning("Script errors: "+errorString);
} catch (ScriptingEngineException ex) {
Logger.error(ex);
throw new InvalidDataException(ex.getMessage(), "");
}
}
- if (job.isOutcomeUsed()) {
+ if (job.isOutcomeSet()) {
Logger.msg(3, "AgentProxy - validating outcome");
String error = validator.validate(job.getOutcomeString());
if (error.length() > 0)
@@ -201,6 +198,7 @@ public class AgentProxy extends EntityProxy * @throws ObjectNotFoundException
* @throws PersistencyException
* @throws ObjectAlreadyExistsException
+ * @throws ScriptErrorException
*/
public void execute(Job job)
throws AccessRightsException,
@@ -208,7 +206,8 @@ public class AgentProxy extends EntityProxy InvalidTransitionException,
ObjectNotFoundException,
PersistencyException,
- ObjectAlreadyExistsException
+ ObjectAlreadyExistsException,
+ ScriptErrorException
{
try {
ItemProxy targetItem = (ItemProxy)Gateway.getProxyManager().getProxy(new EntityPath(job.getItemSysKey()));
diff --git a/src/main/java/com/c2kernel/entity/proxy/ItemProxy.java b/src/main/java/com/c2kernel/entity/proxy/ItemProxy.java index dcaef55..55e1fe9 100644 --- a/src/main/java/com/c2kernel/entity/proxy/ItemProxy.java +++ b/src/main/java/com/c2kernel/entity/proxy/ItemProxy.java @@ -105,7 +105,7 @@ public class ItemProxy extends EntityProxy String outcome = thisJob.getOutcomeString();
// check fields that should have been filled in
if (outcome==null)
- if (thisJob.isOutcomeUsed())
+ if (thisJob.requiresOutcome())
throw new InvalidDataException("Outcome is required.", "");
else
outcome="";
diff --git a/src/main/java/com/c2kernel/lifecycle/WfCastorHashMap.java b/src/main/java/com/c2kernel/lifecycle/WfCastorHashMap.java index 7d88ea9..176f22e 100644 --- a/src/main/java/com/c2kernel/lifecycle/WfCastorHashMap.java +++ b/src/main/java/com/c2kernel/lifecycle/WfCastorHashMap.java @@ -16,7 +16,6 @@ public class WfCastorHashMap extends CastorHashMap put(StateMachine.SKIPPABLE, new Boolean(true));
put(StateMachine.REPEATABLE, new Boolean(true));
put(StateMachine.IGNORABLE, new Boolean(false));
- put("AlwaysUseOutcome", new Boolean(false));
put("Viewpoint", "");
put("Show time", new Boolean(true));
put("Description", "");
diff --git a/src/main/java/com/c2kernel/lifecycle/instance/Activity.java b/src/main/java/com/c2kernel/lifecycle/instance/Activity.java index 8d92522..8dc6b9d 100644 --- a/src/main/java/com/c2kernel/lifecycle/instance/Activity.java +++ b/src/main/java/com/c2kernel/lifecycle/instance/Activity.java @@ -543,26 +543,28 @@ public class Activity extends WfVertex } /**
* Stores the request data as an outcome of the Item It does a great deal of storing outcomes in different configuration
*/ //requestdata is xmlstring
- private String storeOutcome(int eventID, String requestData)
+ private String storeOutcome(int eventID, String requestData, boolean isError) throws InvalidDataException
{
EntityPath entityPath = getItemEntityPath();
if (entityPath != null)
{
- String schemaType = (String) getProperties().get("SchemaType");
- if (schemaType == null || schemaType.length() == 0) // no
- // outcome
- // required
- return null;
- int schemaVersion = 0;
- String versionString = (String) getProperties().get("SchemaVersion");
- try
- {
- schemaVersion = Integer.parseInt(versionString);
+ String schemaType; int schemaVersion;
+ if (isError) {
+ schemaType="Errors";
+ schemaVersion=0;
}
- catch (Exception e)
- {
- Logger.error("Activity.storeOutcome() - invalid schemaVersion " + versionString);
+ else {
+ schemaType = (String) getProperties().get("SchemaType");
+ if (schemaType == null || schemaType.length() == 0) return null;
+ String versionString = (String) getProperties().get("SchemaVersion");
+ try
+ {
+ schemaVersion = Integer.parseInt(versionString);
+ } catch (Exception e) {
+ throw new InvalidDataException("Activity.storeOutcome() - invalid schemaVersion " + versionString, "");
+ }
}
+
Logger.msg(5, "Activity::storeOutcome() - type:" + schemaType + " version:" + schemaVersion);
try
{
@@ -588,16 +590,19 @@ public class Activity extends WfVertex }
else
return null;
- } /** the method to be called by the requestAction() method */
- public void sendEventStoreOutcome(int transitionID, String requestData, AgentPath agent)
+ }
+
+ /** the method to be called by the requestAction() method
+ * @throws InvalidDataException */
+ public void sendEventStoreOutcome(int transitionID, String requestData, AgentPath agent) throws InvalidDataException
{
int eventID = -1;
Event event = null;
event = auditEvent(transitionID, agent);
if (event != null)
eventID = event.getID();
- if (transitionID == Transitions.DONE || transitionID == Transitions.COMPLETE)
- storeOutcome(eventID, requestData);
+ if ((transitionID == Transitions.DONE || transitionID == Transitions.COMPLETE || transitionID == Transitions.SUSPEND) && requestData != null && requestData.length() > 0)
+ storeOutcome(eventID, requestData, transitionID == Transitions.SUSPEND);
EntityPath entityPath = getItemEntityPath();
TransactionManager storage = Gateway.getStorage();
if (entityPath != null)
diff --git a/src/main/java/com/c2kernel/process/UserCodeProcess.java b/src/main/java/com/c2kernel/process/UserCodeProcess.java index 7779802..2f508bc 100644 --- a/src/main/java/com/c2kernel/process/UserCodeProcess.java +++ b/src/main/java/com/c2kernel/process/UserCodeProcess.java @@ -14,6 +14,8 @@ import com.c2kernel.entity.proxy.EntityProxyObserver; import com.c2kernel.entity.proxy.MemberSubscription;
import com.c2kernel.lifecycle.instance.stateMachine.Transitions;
import com.c2kernel.persistency.ClusterStorage;
+import com.c2kernel.scripting.ErrorInfo;
+import com.c2kernel.scripting.ScriptErrorException;
import com.c2kernel.utils.Logger;
/**************************************************************************
@@ -28,6 +30,7 @@ public class UserCodeProcess extends StandardClient implements EntityProxyObserv protected AgentProxy agent;
static boolean active = true;
ArrayList<String> ignoredPaths = new ArrayList<String>();
+ HashMap<String, ErrorInfo> errors = new HashMap<String, ErrorInfo>();
HashMap<String, C2KLocalObject> jobs;
public UserCodeProcess(String agentName, String agentPass) {
@@ -96,18 +99,31 @@ public class UserCodeProcess extends StandardClient implements EntityProxyObserv ignoredPaths.remove(jobKey);
}
else if (thisJob.getPossibleTransition()==Transitions.SUSPEND) {
- if (ignoredPaths.contains(jobKey))
+ if (ignoredPaths.contains(jobKey)) {
+ if (errors.containsKey(jobKey)) {
+ thisJob.setOutcome(Gateway.getMarshaller().marshall(errors.get(jobKey)));
+ errors.remove(jobKey);
+ }
agent.execute(thisJob);
+ }
}
else if (thisJob.getPossibleTransition()==Transitions.RESUME) {
if (!ignoredPaths.contains(jobKey))
agent.execute(thisJob);
}
+ } catch (ScriptErrorException ex) {
+ errors.put(jobKey, ex.getErrors());
+ ignoredPaths.add(jobKey);
} catch (InvalidTransitionException ex) {
// must have already been done by someone else - ignore
} catch (Throwable ex) {
Logger.error("Error executing "+Transitions.getTransitionName(thisJob.getPossibleTransition())+" job:");
Logger.error(ex);
+ ErrorInfo ei = new ErrorInfo();
+ ei.setFatal();
+ ei.addError(ex.getClass().getSimpleName());
+ ei.addError(ex.getMessage());
+ errors.put(jobKey, ei);
ignoredPaths.add(jobKey);
}
}
diff --git a/src/main/java/com/c2kernel/process/module/Module.java b/src/main/java/com/c2kernel/process/module/Module.java index 4a0987a..19cc41a 100644 --- a/src/main/java/com/c2kernel/process/module/Module.java +++ b/src/main/java/com/c2kernel/process/module/Module.java @@ -42,7 +42,7 @@ public class Module { Object result = script.getScript().execute();
if (result instanceof ErrorInfo) {
ErrorInfo error = (ErrorInfo) result;
- Logger.error(error.getErrors());
+ Logger.error(error.toString());
if (error.getFatal())
throw new ScriptingEngineException("Fatal Script Error");
}
diff --git a/src/main/java/com/c2kernel/scripting/ErrorInfo.java b/src/main/java/com/c2kernel/scripting/ErrorInfo.java index 26c0384..efe90ce 100644 --- a/src/main/java/com/c2kernel/scripting/ErrorInfo.java +++ b/src/main/java/com/c2kernel/scripting/ErrorInfo.java @@ -28,13 +28,22 @@ public class ErrorInfo { msg.add(error);
}
- public String getErrors() {
+ @Override
+ public String toString() {
StringBuffer err = new StringBuffer();
for (String element : msg) {
err.append(element+"\n");
}
return err.toString();
}
+
+ public void setErrors(ArrayList<String> msg) {
+ this.msg = msg;
+ }
+
+ public ArrayList<String> getErrors() {
+ return msg;
+ }
public void setFatal() {
fatal=true;
diff --git a/src/main/java/com/c2kernel/scripting/ScriptErrorException.java b/src/main/java/com/c2kernel/scripting/ScriptErrorException.java new file mode 100644 index 0000000..9b1084f --- /dev/null +++ b/src/main/java/com/c2kernel/scripting/ScriptErrorException.java @@ -0,0 +1,30 @@ +package com.c2kernel.scripting;
+
+public class ScriptErrorException extends java.lang.Exception {
+
+ /**
+ * Creates new <code>sciptingEngineException</code> without detail message.
+ */
+ ErrorInfo errors;
+ public ScriptErrorException() {
+ }
+
+ /**
+ * Constructs an <code>sciptingEngineException</code> with the specified detail message.
+ * @param msg the detail message.
+ */
+ public ScriptErrorException(String msg) {
+ super(msg);
+ }
+
+ public ScriptErrorException(ErrorInfo errors) {
+ super(errors.toString());
+ this.errors = errors;
+ }
+
+ public ErrorInfo getErrors() {
+ return errors;
+ }
+}
+
+
diff --git a/src/main/resources/boot/OD/Errors.xsd b/src/main/resources/boot/OD/Errors.xsd new file mode 100644 index 0000000..a30473c --- /dev/null +++ b/src/main/resources/boot/OD/Errors.xsd @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + <xs:element name="Errors"> + <xs:complexType> + <xs:sequence> + <xs:element name="Message" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="fatal" type="xs:boolean"/> + </xs:complexType> + </xs:element> +</xs:schema>
\ No newline at end of file diff --git a/src/main/resources/boot/allbootitems.txt b/src/main/resources/boot/allbootitems.txt index 810d5ee..8c6e052 100644 --- a/src/main/resources/boot/allbootitems.txt +++ b/src/main/resources/boot/allbootitems.txt @@ -7,6 +7,7 @@ OD/PredefinedStepOutcome OD/PropertyDescription
OD/Schema
OD/Script
+OD/Errors
EA/AssignNewVersionFromLast
EA/EditActivityDef
EA/EditSchema
diff --git a/src/main/resources/mapFiles/LifeCycleMap.xml b/src/main/resources/mapFiles/LifeCycleMap.xml index ba75864..f623e52 100644 --- a/src/main/resources/mapFiles/LifeCycleMap.xml +++ b/src/main/resources/mapFiles/LifeCycleMap.xml @@ -97,4 +97,13 @@ <class name="com.c2kernel.lifecycle.instance.predefined.ServerPredefinedStepContainer" extends="com.c2kernel.lifecycle.instance.predefined.PredefinedStepContainer">
<map-to xml="ServerPredefinedStepContainer"/>
</class>
+ <class name="com.c2kernel.scripting.ErrorInfo">
+ <map-to xml="Errors"/>
+ <field name="fatal" type="boolean" direct="false">
+ <bind-xml name="fatal" node="attribute"/>
+ </field>
+ <field name="msg" type="string" direct="false" collection="arraylist" get-method="getErrors" set-method="setErrors">
+ <bind-xml name="Message" node="element"/>
+ </field>
+ </class>
</mapping>
|
