001: /*
002: * The contents of this file are subject to the
003: * Mozilla Public License Version 1.1 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
006: *
007: * Software distributed under the License is distributed on an "AS IS"
008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
009: * See the License for the specific language governing rights and
010: * limitations under the License.
011: *
012: * The Initial Developer of the Original Code is Simulacra Media Ltd.
013: * Portions created by Simulacra Media Ltd are Copyright (C) Simulacra Media Ltd, 2004.
014: *
015: * All Rights Reserved.
016: *
017: * Contributor(s):
018: */
019: package org.openharmonise.rm.commands;
020:
021: import java.util.*;
022: import java.util.logging.*;
023:
024: import org.openharmonise.commons.dsi.AbstractDataStoreInterface;
025: import org.openharmonise.rm.*;
026: import org.openharmonise.rm.config.ConfigSettings;
027: import org.openharmonise.rm.factory.*;
028: import org.openharmonise.rm.publishing.*;
029: import org.openharmonise.rm.resources.AbstractObject;
030: import org.openharmonise.rm.resources.publishing.*;
031: import org.w3c.dom.*;
032:
033: /**
034: * This class processes commands specified in HaRP to be executed within
035: * Harmonise and possibly on a Harmoniseobject. Multiple commands and
036: * objects can be handled and the output will be published to HaRP.
037: *
038: * @author Michael Bell
039: * @version $Revision: 1.3 $
040: *
041: */
042: public class CommandProcessHandler implements Publishable {
043:
044: //XML constants
045: /**
046: * The 'WorkflowEvent' tag name
047: */
048: public static final String TAG_WORKFLOW = "WorkflowEvent";
049:
050: /**
051: * The 'jump'/'skip' command tag name
052: */
053: public static final String JMP_CMD = "Jmp";
054:
055: /**
056: * The 'jump if'/'skip if' command tag name
057: */
058: public static final String JMPIF_CMD = "JmpIf";
059:
060: /**
061: * The 'stop' command tage name
062: */
063: public static final String STOP_CMD = "Stop";
064:
065: /**
066: * The standard command namespace for the Harmonise framework, this namespace
067: * forms the prefix to all the standard command class names, for example
068: * the 'Save' command is represented by a class of the name
069: * COMMANDS_NAMESPACE + "Save'
070: */
071: public static final String COMMANDS_NAMESPACE = AbstractCmd.class
072: .getPackage().getName()
073: + ".Cmd";
074:
075: /**
076: * Prefix for configuration parameters which specify project specific
077: * command classes
078: */
079: public static final String PROJECT_COMMAND = "PROJECT_CMD_";
080:
081: /**
082: * The XML element resultant of the command processing
083: */
084: private Element m_workflow_output;
085:
086: /**
087: * The owner document of the resultant XML of the command processing
088: */
089: private HarmoniseOutput m_workflow_output_document;
090:
091: /**
092: * The data store interface to be used in command processing
093: */
094: private AbstractDataStoreInterface m_dsi;
095:
096: /**
097: * The state context for the command processing
098: */
099: private State m_state = null;
100:
101: /**
102: * The list of names of all commands to be processed.
103: */
104: private List m_cmd_names;
105:
106: /**
107: * The list of command parameters.
108: */
109: private List m_cmd_parameters;
110:
111: /**
112: * Logger for this class.
113: */
114: private static final Logger m_logger = Logger
115: .getLogger(CommandProcessHandler.class.getName());
116:
117: /**
118: * Creates a new command process handler with a an interface to
119: * the data store.
120: *
121: * @param dsi the data store interface
122: */
123: public CommandProcessHandler(AbstractDataStoreInterface dsi) {
124: m_dsi = dsi;
125: }
126:
127: /**
128: * Returns the <code>State</code> associated to this command processor.
129: *
130: * @return the <code>State</code> associated to this command processor.
131: */
132: public State getState() {
133: return m_state;
134: }
135:
136: /**
137: * Returns the XML document associated to this command processor.
138: *
139: * @return the XML document associated to this command processor.
140: */
141: public org.w3c.dom.Document getXMLDocument() {
142: return m_workflow_output_document;
143: }
144:
145: /* (non-Javadoc)
146: * @see org.openharmonise.rm.publishing.Publishable#publish(org.openharmonise.rm.resources.publishing.Template, org.openharmonise.rm.publishing.HarmoniseOutput, org.openharmonise.rm.publishing.State)
147: */
148: public Element publish(Template template, HarmoniseOutput output,
149: State state) throws PublishException {
150: Element resultEl = null;
151:
152: try {
153: resultEl = publish(template.getTemplateRootElement(),
154: output, state);
155: } catch (DataAccessException e) {
156: throw new PublishException(e.getLocalizedMessage());
157: }
158:
159: return resultEl;
160: }
161:
162: /* (non-Javadoc)
163: * @see org.openharmonise.rm.publishing.Publishable#publish(org.w3c.dom.Element, org.openharmonise.rm.publishing.HarmoniseOutput, org.openharmonise.rm.publishing.State)
164: */
165: public Element publish(Element topEl, HarmoniseOutput output,
166: State state) throws PublishException {
167: if (topEl.getTagName().equalsIgnoreCase(
168: CommandProcessHandler.TAG_WORKFLOW) == false) {
169: throw new InvalidXMLElementException("Workflow tag needed");
170: }
171:
172: Element returnEl = null;
173:
174: List wrkflowObjs;
175: try {
176: wrkflowObjs = HarmoniseObjectFactory
177: .instantiateWorkflowObjects(this .m_dsi, topEl,
178: state);
179: } catch (HarmoniseFactoryException e) {
180: throw new PublishException(
181: "Error occured getting objects from factory", e);
182: }
183:
184: m_workflow_output_document = output;
185: m_state = state;
186: m_cmd_names = new ArrayList();
187: m_cmd_parameters = new ArrayList();
188:
189: m_workflow_output = output
190: .createElement(CommandProcessHandler.TAG_WORKFLOW);
191:
192: NodeList event_nodes = topEl
193: .getElementsByTagName(AbstractCmd.TAG_COMMAND);
194:
195: for (int i = 0; i < event_nodes.getLength(); i++) {
196: // Cmd names
197: Element this _workflow_element = (Element) event_nodes
198: .item(i);
199:
200: String cmd_name = this _workflow_element
201: .getAttribute(AbstractObject.ATTRIB_NAME);
202:
203: if ((cmd_name == null) || (cmd_name.length() == 0)) {
204: throw new InvalidXMLElementException(
205: "Command names for workflow events must be given in the "
206: + AbstractObject.ATTRIB_NAME
207: + " tag of the "
208: + AbstractCmd.TAG_COMMAND
209: + " element. " + " cmd_name:"
210: + cmd_name);
211: }
212:
213: m_cmd_names.add(cmd_name);
214:
215: m_cmd_parameters
216: .add(getWorkflowParameters(this _workflow_element
217: .getChildNodes()));
218: }
219:
220: Iterator iter = wrkflowObjs.iterator();
221:
222: while (iter.hasNext()) {
223: Object wf_obj = (Object) iter.next();
224: executeCommands(wf_obj);
225: }
226:
227: return m_workflow_output;
228: }
229:
230: /* (non-Javadoc)
231: * @see org.openharmonise.rm.publishing.Publishable#populate(org.w3c.dom.Element, org.openharmonise.rm.publishing.State)
232: */
233: public void populate(Element xmlElement, State state)
234: throws PopulateException {
235: //never going to be populating - are we?
236:
237: }
238:
239: /* (non-Javadoc)
240: * @see org.openharmonise.rm.publishing.Publishable#getTagName()
241: */
242: public String getTagName() {
243: return TAG_WORKFLOW;
244: }
245:
246: /*----------------------------------------------------------------------------
247: Protected methods
248: -----------------------------------------------------------------------------*/
249:
250: /**
251: * Executes the loaded commands on the specified object.
252: *
253: * @param commandObject the object commands are to be executed on
254: * @throws PublishException if any errors occur, caused either by a
255: * reference to an invalid command, invalid object or if there is
256: * an error executing the command
257: */
258: protected void executeCommands(Object commandObject)
259: throws PublishException {
260:
261: boolean cmd_successful = true;
262: String cmd_name;
263: int cmd_execution_point = 0;
264:
265: Context context = new Context();
266:
267: while (cmd_execution_point < m_cmd_parameters.size()) {
268: // get the next command and arguments
269: cmd_name = (String) (m_cmd_names.get(cmd_execution_point));
270:
271: // allow cmd_args to be null if there aren't any
272: Map cmd_parameter_map;
273:
274: if (m_cmd_parameters.size() >= cmd_execution_point) {
275: cmd_parameter_map = (Map) m_cmd_parameters
276: .get(cmd_execution_point);
277: } else {
278: cmd_parameter_map = null;
279: }
280:
281: cmd_execution_point++;
282:
283: //check for skipping or jumping rules
284: if (cmd_name.startsWith("-")) {
285: String scmd_name = cmd_name.substring(1); // remove intial -
286:
287: int param_at_idx = scmd_name.indexOf('-');
288:
289: String sparam_value;
290:
291: if (param_at_idx != -1) {
292: scmd_name = cmd_name.substring(1, param_at_idx + 1);
293: // remove command within - -
294: sparam_value = cmd_name.substring(param_at_idx + 2);
295: } else {
296: sparam_value = "";
297: }
298:
299: if (scmd_name.equals(JMP_CMD)
300: || (scmd_name.equals(JMPIF_CMD) && cmd_successful)) {
301:
302: cmd_execution_point += getNumberOfCommandsToSkip(sparam_value);
303:
304: continue;
305: } else if ((scmd_name.equals(JMPIF_CMD) && !cmd_successful)) {
306:
307: continue;
308: } else if (scmd_name.equals(STOP_CMD)) {
309:
310: break;
311: }
312: }
313:
314: // Get command classname
315: String classname;
316: String proj_cmd = PROJECT_COMMAND + cmd_name;
317:
318: try {
319: classname = ConfigSettings.getProperty(proj_cmd);
320: } catch (Exception e) {
321: classname = COMMANDS_NAMESPACE + cmd_name;
322: }
323:
324: if (classname == null) {
325: classname = COMMANDS_NAMESPACE + cmd_name;
326: }
327:
328: AbstractCmd cmd = null;
329:
330: try {
331: Class cls = Class.forName(classname);
332: cmd = (AbstractCmd) cls.newInstance();
333: } catch (InstantiationException e) {
334: throw new PublishException(
335: "Cannot instantiate command class " + classname,
336: e);
337: } catch (ClassNotFoundException e) {
338: throw new PublishException(
339: cmd_name
340: + " is not a valid workflow command, expect command class "
341: + classname, e);
342: } catch (IllegalAccessException e) {
343: throw new PublishException("Illegal access to class", e);
344: }
345:
346: if (cmd.isValidCommandObject(commandObject) == true) {
347:
348: if (commandObject != null) {
349: cmd.setCommandObject(commandObject);
350: }
351:
352: cmd.setParameters(cmd_parameter_map);
353: cmd.setDataStoreInteface(m_dsi);
354:
355: if (m_state != null) {
356: cmd.setState(m_state);
357: }
358:
359: Object cmdResult = null;
360: try {
361: cmdResult = cmd.execute(context);
362: } catch (CommandException e) {
363: throw new PublishException(
364: "Error executing command", e);
365: }
366:
367: String sTemplateId = cmd
368: .getParameter(AbstractCmd.PARAM_TEMPLATE_ID);
369: int nTemplateId = -1;
370:
371: if (sTemplateId != null) {
372:
373: nTemplateId = Integer.parseInt(sTemplateId);
374: if (nTemplateId > 0) {
375: try {
376: Template template = (Template) HarmoniseObjectFactory
377: .instantiateHarmoniseObject(m_dsi,
378: Template.class.getName(),
379: nTemplateId);
380:
381: if (cmdResult instanceof Publishable) {
382: Publishable pObj = (Publishable) cmdResult;
383:
384: // instantaite template
385: addResult(pObj.publish(template,
386: m_workflow_output_document,
387: m_state));
388:
389: } else if (cmdResult instanceof Collection) {
390: Collection collObj = (Collection) cmdResult;
391:
392: Iterator iter = collObj.iterator();
393:
394: while (iter.hasNext()) {
395: Object obj = iter.next();
396:
397: if (obj instanceof Publishable) {
398: Publishable pObj = (Publishable) obj;
399: addResult(pObj
400: .publish(
401: template,
402: m_workflow_output_document,
403: m_state));
404:
405: }
406: }
407: }
408: } catch (HarmoniseFactoryException e) {
409: throw new PublishException(
410: "Error getting template from Factory",
411: e);
412: }
413: }
414: }
415: String out_text = cmd
416: .getParameter(AbstractCmd.PARAM_OUT_TEXT);
417: addSimpleTextElement(WebPage.TAG_ANCILLARY_TEXT,
418: out_text);
419: }
420: }
421:
422: }
423:
424: /**
425: * Adds a result child element, the result of publishing the result
426: * of one command execution, to the overall result element of this
427: * command process.
428: *
429: * @param result_root the result XML element
430: */
431: protected void addResult(Element result_root) {
432: m_workflow_output.appendChild(m_workflow_output_document
433: .importNode((Node) result_root, true));
434: }
435:
436: /**
437: * Returns a <code>Map</code> of the workflow parameters represented in the
438: * <code>NodeList</code>.
439: *
440: * @param param_nodes the list of nodes representing parameters
441: * @return a <code>Map</code> of the workflow parameters represented in the
442: * <code>NodeList</code>
443: */
444: protected Map getWorkflowParameters(NodeList param_nodes) {
445: // get params from template
446: HashMap cmd_params = new HashMap();
447:
448: for (int j = 0; j < param_nodes.getLength(); j++) {
449: if (param_nodes.item(j).getNodeType() != Node.ELEMENT_NODE) {
450: continue;
451: }
452:
453: Element current_element = (Element) param_nodes.item(j);
454:
455: if (!current_element.getTagName().equals(
456: AbstractCmd.TAG_COMMAND_PARAM)) {
457: throw new RuntimeException(
458: "Workflow event must only contain tags named:"
459: + AbstractCmd.TAG_COMMAND_PARAM
460: + " the node:" + current_element
461: + " is not allowed ");
462: }
463:
464: String param_name = current_element
465: .getAttribute(AbstractObject.ATTRIB_NAME);
466:
467: Text txt_node = (Text) current_element.getFirstChild();
468: String param_value = "";
469:
470: if (txt_node != null) {
471: param_value = txt_node.getNodeValue();
472: }
473:
474: // Allow multiple values for the prameters (Hashmap to Name-Vector_of_values)
475: Vector existing_values = (Vector) cmd_params
476: .get(param_name);
477:
478: if (existing_values == null) {
479: existing_values = new Vector();
480: cmd_params.put(param_name, existing_values);
481: }
482:
483: existing_values.addElement(param_value);
484: }
485:
486: return cmd_params;
487: }
488:
489: /*----------------------------------------------------------------------------
490: Private methods
491: -----------------------------------------------------------------------------*/
492:
493: /**
494: * Adds a text element to the result element of the command processor
495: * under an XML element with the specified tag name.
496: *
497: * @param tag_name the tag name
498: * @param tag_txt the text to add
499: */
500: private void addSimpleTextElement(String tag_name, String tag_txt) {
501: Element simple_elm = m_workflow_output_document
502: .createElement(tag_name);
503: Text simple_text = m_workflow_output_document
504: .createTextNode(tag_txt);
505:
506: simple_elm.appendChild(simple_text);
507:
508: addResult(simple_elm);
509: }
510:
511: /**
512: * Returns the number of commands to skip.
513: *
514: * @param scmd_param <code>String</code> representation of number of commands to skip
515: *
516: * @return the number of commands to skip
517: */
518: private int getNumberOfCommandsToSkip(String scmd_param) {
519: int num_to_skip = 0;
520:
521: try {
522: num_to_skip = Integer.parseInt(scmd_param);
523: } catch (NumberFormatException e) {
524: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
525: }
526:
527: return num_to_skip;
528: }
529: }
|