All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.openwfe.org.engine.expressions.SubProcessRefExpression Maven / Gradle / Ivy

There is a newer version: 1.7.2
Show newest version
/*
 * Copyright (c) 2001-2006, John Mettraux, OpenWFE.org
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 * . Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.  
 * 
 * . Redistributions in binary form must reproduce the above copyright notice, 
 *   this list of conditions and the following disclaimer in the documentation 
 *   and/or other materials provided with the distribution.
 * 
 * . Neither the name of the "OpenWFE" nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id: SubProcessRefExpression.java 2846 2006-06-20 21:58:03Z jmettraux $
 */

//
// SubProcessRefExpression.java
//
// [email protected]
//
// generated with 
// jtmpl 1.0.04 20.11.2001 John Mettraux ([email protected])
//

package openwfe.org.engine.expressions;

import openwfe.org.Utils;
import openwfe.org.OpenWfeException;
import openwfe.org.ApplicationContext;
import openwfe.org.xml.XmlUtils;
import openwfe.org.time.Time;
import openwfe.org.engine.Definitions;
import openwfe.org.engine.expool.PoolException;
import openwfe.org.engine.expool.ExpressionPool;
import openwfe.org.engine.launch.LaunchException;
import openwfe.org.engine.listen.reply.OkReply;
import openwfe.org.engine.history.History;
import openwfe.org.engine.workitem.Attribute;
import openwfe.org.engine.workitem.LaunchItem;
import openwfe.org.engine.workitem.InFlowWorkItem;
import openwfe.org.engine.workitem.StringAttribute;
import openwfe.org.engine.workitem.StringMapAttribute;
import openwfe.org.engine.expressions.raw.RawExpression;


/**
 * A leaf (terminal) expression : a reference to a subProcess
 *
 * 

CVS Info : *
$Author: jmettraux $ *
$Date: 2006-06-20 23:58:03 +0200 (Tue, 20 Jun 2006) $ *
$Id: SubProcessRefExpression.java 2846 2006-06-20 21:58:03Z jmettraux $
* * @author [email protected] */ public class SubProcessRefExpression extends ZeroChildExpression implements WithChildren { private final static org.apache.log4j.Logger log = org.apache.log4j.Logger .getLogger(SubProcessRefExpression.class.getName()); // // CONSTANTS /** * Instructs the expression to look for the subprocess name in the given * variable. */ public final static String A_VARIABLE_REF = "variable-ref"; /** * Instructs the expression to look for the subprocess name in the given * field of the applied workitem. */ public final static String A_FIELD_REF = "field-ref"; /** * The name of the subprocess to launch is given directly as a ref. * It may also be possible that this ref contains a dollar notation * syntax referencing a variable or a field like in "${target}" or * "${field:target}" or even "${field:target_prefix}_${index}".
* See the 'dollar notation' in the documentation. */ public final static String A_REF = "ref"; /** * When the attribute 'forget' of this expression is set to 'true' (or * 'yes'), then the flow initiating the subprocess won't block until * it finishes, it won't wait on it. */ public final static String A_FORGET = "forget"; /** * When the attribute 'fields' is present, the workitem in use in the * new subprocess instance will be empty except for the fields named as * value of this attribute. * If this attribute has an empty value, the workitem will have no fields * at all. * If this attribute is not present, the workitem will be a copy of the * workitem in use in the parent flow. */ public final static String A_FIELDS = "fields"; /** * In the subprocess itself, the variable __cparams__ will contain an * XML representation of the children of the subprocess as called. */ public final static String V_CPARAMS = "__cparams__"; /** * '::' is used in workflow definition URLs like * "mainEngine::http://flowdef.server.company/flow1.xml" or * "secondaryEngine::http://flowdef.server.other.company/flow3.xml", * it separates the name of the engine supposed to run the flow from * the URL of the definition itself. */ public final static String ENGINE_SEPARATOR = "::"; // // FIELDS private FlowExpressionId subId = null; private boolean forgetting = false; private java.util.Map launchVariables = null; private ChildrenTracker tracker = null; // // CONSTRUCTORS // // BEAN METHODS /* * debug * public void setParent (final FlowExpressionId fei) { log.debug("setParent() \n to "+fei+"\n for "+this.getId()); super.setParent(fei); } */ public FlowExpressionId getSubId () { return this.subId; } public void setSubId (final FlowExpressionId fei) { this.subId = fei; } public boolean isForgetting () { return this.forgetting; } public void setForgetting (final boolean b) { this.forgetting = b; } public java.util.Map getLaunchVariables () { return this.launchVariables; } public void setLaunchVariables (final java.util.Map m) { this.launchVariables = m; } public ChildrenTracker getTracker () { return this.tracker; } public void setTracker (final ChildrenTracker ct) { this.tracker = ct; } // // METHODS from FlowExpression /** * Either launches a subProcess, either launches an external process */ public void apply (final InFlowWorkItem wi) throws ApplyException { /* log.debug("apply() id : "+this.getId()); log.debug("apply() parent : "+this.getParent()); log.debug("apply() next : "+this.getNext()); */ // // preparation this.forgetting = shouldForget(wi); // ref String ref = determineReference(wi); ref = checkIfLocalEngine(ref); if (log.isDebugEnabled()) log.debug("apply() ref >"+ref+"< for "+this.getId()); if (isUrl(ref)) { outerLaunch(wi, ref); return; } innerLaunch(wi, ref); } /** * If the launch was done, will reply to the parent expression, else * it will trigger the launch (if all children did reply). * This method is synchronized to avoid collision between children replies. */ public synchronized void reply (final InFlowWorkItem wi) throws ReplyException { if (log.isDebugEnabled()) log.debug("reply() from "+wi.getLastExpressionId()); //xmlDebugDump("reply"); // // some debug output if (this.tracker == null) { log.debug("reply() no tracker : replying to parent."); replyToParent(wi); return; } // // a child replies... this.tracker.reply(wi); if (this.tracker.hasNext()) { log.debug("reply() applying next child"); this.tracker.applyNext(this); return; } // // fetch the rest of the content of the tracker this.launchVariables.put //(A_CONTENT, this.tracker.getRemainingContent()); (V_CPARAMS, this.tracker.getRemainingContent()); //log.debug // ("reply() remainingContent : \n"+ // this.tracker.getRemainingContent()); // // fetch the results from the children evaluation this.launchVariables.putAll(this.tracker.getResults()); // // children did reply, now do the launch... try { doLaunch(this.tracker.getWorkitem()); } catch (final ApplyException ae) { throw new ReplyException ("'after children evaluation' sublaunch failure", ae); } this.tracker = null; // // nullifying tracker so that next call to reply() will // end up in a call to the parent expression. } /** * Launches a flow whose definition has to be fetched from a URL. * Such flows never cares about 'nested children'. */ protected void outerLaunch (final InFlowWorkItem wi, final String ref) throws ApplyException { final InFlowWorkItem launchWi = prepareSubWorkitem(wi); FlowExpressionId parentId = this.getId(); //this.launchDone = true; if (isForgetting()) // // no parent id needed for subflow // parentId = null; else // // store itself, subprocess will reply // (potentially very late, thus a bit of persistence is required) // this.storeItself(); // // history logging historyLog (wi, History.EVT_FLOW_START, null, "launching ref=\""+ref+"\""); // // do the launch try { getLauncher().launch(launchWi, parentId, ref, areLaunchesAsync()); // there are no variables passed to 'outer' subprocesses } catch (final LaunchException le) { throw new ApplyException ("outer launch failure "+ref, le); } if (this.isForgetting()) // // don't wait for any reply from the subprocess just launched // applyToParent(wi); } /** * Launches a flow whose definition is an already know subprocess * (already bound locally or globally (engine-level)). */ public void innerLaunch (final InFlowWorkItem wi, final String ref) throws ApplyException { // // launching if (log.isDebugEnabled()) log.debug("innerLaunch() ref is \""+ref+"\""); final Object oValue = lookupVariable(ref); if (oValue == null) { throw new ApplyException ("ref '"+ref+"' points to nothing."); } if (log.isDebugEnabled()) { log.debug ("innerLaunch() oValue.class "+oValue.getClass().getName()); } if ( ! (oValue instanceof FlowExpressionId) && ! (oValue instanceof RawExpression)) { log.warn ("innerLaunch() oValue class : "+oValue.getClass().getName()); throw new ApplyException ("ref '"+ref+"' doesn't point to a subprocess."); } RawExpression rawSub = null; if (oValue instanceof RawExpression) { rawSub = (RawExpression)oValue; rawSub = (RawExpression)rawSub.clone(); rawSub.getId().setWorkflowInstanceId (RawExpression.determineNewWorkflowInstanceId()); try { getExpressionPool().add(rawSub); } catch (final PoolException e) { throw new ApplyException ("Failed to store new template into expression pool", e); } this.subId = rawSub.getId(); } else { this.subId = (FlowExpressionId)oValue; final FlowExpression re = getExpressionPool().fetch(this.subId); if (re == null) { throw new ApplyException ("innerLaunch() "+ "subprocess '"+ref+ "' points at "+this.subId+ " but it's gone from the expool"); } if (log.isDebugEnabled()) { log.debug ("innerLaunch() "+this.subId+ " is of class "+re.getClass().getName()); } /* if ( ! (re instanceof RawExpression)) { log.debug ("cannot launch subprocess out of "+ re.getClass().getName()); Utils.logStackTrace(log, "innerLaunch()"); return; } */ rawSub = (RawExpression)re; } if (log.isDebugEnabled()) log.debug("innerLaunch() inner subprocess at "+this.subId); // // feeding attributes into variables this.launchVariables = new java.util.HashMap(); final java.util.Iterator it = this.getAttributes().keySet().iterator(); while (it.hasNext()) { final String key = (String)it.next(); this.launchVariables.put(key, lookupAttribute(key, wi)); //log.debug("innerLaunch() put variable '"+key+"'"); } //final String[] sChildrenToMap = rawSub.getChildrenToMap(); final String s = rawSub.lookupAttribute(DefineExpression.A_MAP_CHILDREN, wi); if (log.isDebugEnabled()) log.debug("innerLaunch() map-children=\""+s+"\""); String[] sChildrenToMap = null; if (s != null) sChildrenToMap = s.split(",\\s*"); if (sChildrenToMap == null || sChildrenToMap.length < 1) { log.debug("innerLaunch() no children to map, launching directly"); doLaunch(wi); return; } else if (log.isDebugEnabled()) { log.debug ("innerLaunch() childrenToMap.length : "+sChildrenToMap.length); } // // if (sChildrenToMap.length > 0) final String sContent = (String)this.launchVariables.get(A_CONTENT); this.tracker = new ChildrenTracker(); //log.debug("innerLaunch() new tracker : "+this.tracker); this.tracker.init(sContent, sChildrenToMap, wi); //this.storeItself(); // // done in the tracker // begin tracking children try { this.tracker.applyNext(this); } catch (final ReplyException re) { throw new ApplyException ("failed to begin with children evaluation", re); } } /** * This method is called by reply() when all the (nested) children * replied. */ protected void doLaunch (final InFlowWorkItem wi) throws ApplyException { FlowExpressionId parentId = this.getId(); if (this.isForgetting()) { parentId = null; } if (log.isDebugEnabled()) { log.debug("doLaunch() this.id is "+this.getId()); log.debug("doLaunch() this.parent is "+this.getParent()); log.debug("doLaunch() parentId is "+parentId); } // // making sure that the variable __cparams__ is set String sContent = (String)this.launchVariables.get(A_CONTENT); if (sContent != null && ( ! this.launchVariables.keySet().contains(V_CPARAMS))) { this.launchVariables.put(V_CPARAMS, sContent); if (log.isDebugEnabled()) log.debug("doLaunch() cparams from content : "+sContent); } // // launching try { getLauncher().launchSub ((InFlowWorkItem)wi.clone(), parentId, this.subId, // template id this.launchVariables, areLaunchesAsync()); } catch (final LaunchException le) { throw new ApplyException ("Failed to launch inner subprocess "+this.subId, le); } if (this.isForgetting()) // // don't wait for a reply of the subprocess // applyToParent(wi); } /* * Checks whether a ref is a URL or not (it may also comprise * a 'engineId::' prefix). */ private boolean isUrl (final String ref) throws ApplyException { if (ref == null || ref.length() < 1) { throw new ApplyException ("Cannot launch null or \"\" flow url"); } if (Utils.isUrl(ref)) return true; final int i = ref.indexOf("::"); if (i > -1) { if (i+2 == ref.length()) return false; // avoiding 'out of index'... return Utils.isUrl(ref.substring(i+2)); } return false; } // // METHODS overridden from FlowExpression /** * this method is overriden to allow later resolution of the referenced * subProcess (ie linking of the expression to the references subProcess). */ public void setAttributes (java.util.Map m) { super.setAttributes(m); } // // METHODS /** * Returns true if flow launches should return immediately (this info * is fetched from the engine's application context). */ protected boolean areLaunchesAsync () { return context().getBoolean(ExpressionPool.P_ASYNC_LAUNCH, true); } /* * This method will remove any 'engine::' prefix if engine is * the id of the local engine * (thus the launch will be internal) * * (should help fix bug #928820) */ private String checkIfLocalEngine (final String reference) { if (reference.indexOf(ENGINE_SEPARATOR) < 0) return reference; final String localEngineId = context().getApplicationName(); if (localEngineId == null) { log.warn ("application param '"+Definitions.ENGINE_ID+ "' is missing, cannot determine engineId"); return reference; } String[] ss = reference.split("::"); if (ss[0].equals(localEngineId)) return ss[1]; return reference; } /** * Given expression attributes, workitem fields and this expression * variables, determines the ref, ie the subprocess name or url that * is supposed to be launched. */ protected String determineReference (final InFlowWorkItem wi) throws ApplyException { final String varName = lookupAttribute(A_VARIABLE_REF, wi); if (varName != null) { String ref = (String)lookupVariable(varName); if (ref == null) { throw new ApplyException ("no variable set under name '"+varName+"'"); } return ref; } final String fieldName = lookupAttribute(A_FIELD_REF, wi); if (fieldName != null) { Attribute aRef = wi.getAttributes().get(fieldName); String ref = null; if (aRef != null) ref = aRef.toString(); if (ref == null) { throw new ApplyException ("workitem holds no field named '"+fieldName+"'"); } return ref; } final String ref = lookupAttribute(A_REF, wi); if (ref == null) { throw new ApplyException ("No 'variable-ref', 'field-ref' or 'ref' attribute in "+ "'subprocess' expression"); } return ref; } private void addFieldsFromRegex (final String regex, final InFlowWorkItem parentWi, final InFlowWorkItem newWi) { final java.util.Iterator it = parentWi.getAttributes().keyNamesMatching(regex).iterator(); while (it.hasNext()) { final String fieldName = ((String)it.next()).trim(); if (log.isDebugEnabled()) log.debug("prepareFields() adding field '"+fieldName+"'"); final Attribute a = parentWi.getAttribute(fieldName); newWi.getAttributes().put(new StringAttribute(fieldName), a); } } private void addFieldWithValue (final String assignation, //final InFlowWorkItem parentWi, final InFlowWorkItem newWi) { final String[] ss = assignation.split(" *= *"); final String fieldName = ss[0].trim(); final String value = ss[1].trim(); if (log.isDebugEnabled()) { log.debug ("addFieldWithValue() newWi['"+fieldName+"'] = '"+value+"'"); } newWi.getAttributes().put (new StringAttribute(fieldName), new StringAttribute(value)); } private void addFieldAlias (final String aliasing, final InFlowWorkItem parentWi, final InFlowWorkItem newWi) { final String[] ss = aliasing.split(" *as *"); final String newFieldName = ss[0].trim(); final String parentFieldName = ss[1].trim(); final Attribute parentValue = parentWi.getAttribute(parentFieldName); if (parentValue != null) { newWi.getAttributes() .put(new StringAttribute(newFieldName), parentValue); } } private InFlowWorkItem prepareSubWorkitem (final InFlowWorkItem parentWi) { final String sFields = lookupAttribute(A_FIELDS, parentWi); final InFlowWorkItem newWi = (InFlowWorkItem)parentWi.clone(); if (sFields == null) return newWi; newWi.setAttributes(new StringMapAttribute()); String[] fields = sFields.split(", *"); for (int i=0; i -1) addFieldAlias(fields[i], parentWi, newWi); else addFieldsFromRegex(fields[i], parentWi, newWi); } return newWi; } /* * wi.field > var > wfd */ private boolean shouldForget (final InFlowWorkItem wi) { // may be dangerous ??!! if (Utils.toBoolean(wi.getAttribute(A_FORGET))) return true; if (Utils.toBoolean(lookupVariable(A_FORGET))) return true; if (Utils.toBoolean(lookupAttribute(A_FORGET, wi))) return true; return false; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy