/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: ProcBasedImpl.java 2368 2007-05-03 21:58:25Z mlipp $
*
* $Log$
*/
package de.danet.an.workflow.domain;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import de.danet.an.util.sax.StackedHandler;
import de.danet.an.workflow.internalapi.ExtActivityLocal;
import de.danet.an.workflow.internalapi.ExtProcessLocal;
import de.danet.an.workflow.localapi.ActivityLocal;
import de.danet.an.workflow.localapi.ProcessLocal;
import de.danet.an.workflow.localapi.ProcessDefinitionDirectoryLocal;
import de.danet.an.workflow.localcoreapi.WfProcessLocal;
import de.danet.an.workflow.omgcore.AlreadyRunningException;
import de.danet.an.workflow.omgcore.CannotCompleteException;
import de.danet.an.workflow.omgcore.CannotStartException;
import de.danet.an.workflow.omgcore.CannotStopException;
import de.danet.an.workflow.omgcore.InvalidDataException;
import de.danet.an.workflow.omgcore.InvalidRequesterException;
import de.danet.an.workflow.omgcore.NotEnabledException;
import de.danet.an.workflow.omgcore.NotRunningException;
import de.danet.an.workflow.omgcore.ProcessData;
import de.danet.an.workflow.omgcore.RequesterRequiredException;
import de.danet.an.workflow.omgcore.UpdateNotAllowedException;
import de.danet.an.workflow.omgcore.WfRequester;
import de.danet.an.workflow.api.DefaultProcessData;
import de.danet.an.workflow.api.FormalParameter;
import de.danet.an.workflow.api.InvalidKeyException;
import de.danet.an.workflow.api.ProcessDefinition;
import de.danet.an.workflow.api.Activity.SubFlowImplementation;
/**
* This class represents a sub-process based activity implementation,
* i.e. it implements {@link
* de.danet.an.workflow.localapi.ActivityLocal.SubFlowImplementation
* <code>SubFlowImplementation</code>}.
*
* @author <a href="mailto:mnl@mnl.de">Michael N. Lipp</a>
* @version $Revision: 2368 $
*/
public final class ProcBasedImpl
extends ActImplBase implements SubFlowImplementation {
/** Unique id. */
static final long serialVersionUID = 5223962486120295143L;
private static final org.apache.commons.logging.Log logger
= org.apache.commons.logging.LogFactory.getLog(ProcBasedImpl.class);
private String packageId;
private String processId;
private int execMode = SYNCHR;
/** The actual parameters. */
private Object[] actualParams = null;
private String startedProcess = null;
/**
* Creates an instance of <code>ProcBasedImpl</code>
* with all attributes initialized to default values.
*/
private ProcBasedImpl () {
}
/* Comment copied from interface. */
public String packageId() {
return packageId;
}
/* Comment copied from interface. */
public String processId() {
return processId;
}
/* Comment copied from interface. */
public String processKey () {
return startedProcess;
}
/**
* The key is not persisted persisted here. Rather it is part of the
* persistent state of the invoking activity and restored when the
* implementation is retrieved.
*/
public void setProcessKey (String key) {
startedProcess = key;
}
/**
* Return the actual parameters.
* @return actualParameters.
*/
public Object[] actualParameters() {
return actualParams;
}
/* Comment copied from interface. */
public int execution() {
return execMode;
}
/**
* Triggers the execution of a subprocess for the given activity.
* @param act the activity
*/
public void invoke (ExtActivityLocal act) {
try {
try {
ExtProcessLocal process = (ExtProcessLocal)act.containerLocal();
ProcessDefinitionDirectoryLocal pdd
= (ProcessDefinitionDirectoryLocal)
process.processDefinitionDirectoryLocal ();
WfRequester req = new SubProcRequester (act, execMode);
ProcessData pd = new DefaultProcessData
(parameterMap (process, act, pdd));
WfProcessLocal p
= pdd.createProcessLocal (packageId(), processId(), req);
p.setProcessContext(pd);
p.start ();
startedProcess = p.key ();
if (execMode == ASYNCHR) {
act.complete ();
}
} catch (InvalidDataException e) {
act.terminate ();
} catch (UpdateNotAllowedException e) {
act.terminate ();
} catch (AlreadyRunningException e) {
act.terminate ();
} catch (CannotStartException e) {
act.terminate ();
} catch (NotEnabledException e) {
act.terminate ();
} catch (InvalidKeyException e) {
act.terminate ();
} catch (InvalidRequesterException e) {
act.terminate ();
} catch (RequesterRequiredException e) {
act.terminate ();
} catch (CannotCompleteException e) {
act.terminate ();
}
return;
} catch (NotRunningException e) {
// cannot happen
logger.error (e.getMessage (), e);
} catch (CannotStopException e) {
// cannot happen
logger.error (e.getMessage (), e);
}
}
/**
* Create the parameter map for sub-process based on the
* activity's context, the formal parameters of the sub-process
* and the values of the actual parameters.<P>
*
* Note that all parameters could be derived from the activity.
* This method is intended to be used in cases where the additional
* parameters are available anyway.
*
* @param process the containing process
* @param act the activity to which this tool implementation description
* is to be applied
* @param pdd the process definition directory
* @return the parameter map
*/
public Map parameterMap
(ExtProcessLocal process, ActivityLocal act, ProcessDefinitionDirectoryLocal pdd) {
try {
ProcessDefinition pd
= pdd.lookupProcessDefinition(packageId(), processId());
FormalParameter[] fps = pd.formalParameters();
return parameterMap (process, act, fps);
} catch (InvalidKeyException e) {
// cannot happen since procdef is initially verified
logger.error (e.getMessage(), e);
throw new IllegalStateException (e.getMessage());
}
}
/**
* Merge the result returned by an implementation into the process
* data. The names of the result data items must be formal parameter
* names which are mapped to process data items as defined by the
* actual parameters.
*
* @param act the activity related with the implementation invocation.
* @param result the result data.
* @throws InvalidDataException if the entries in the result do not
* match formal parameter names or excessive entries exist.
*/
public void mergeResult (ActivityLocal act, Map result)
throws InvalidDataException {
if (!(result instanceof ProcessDataWithParams)) {
throw new InvalidDataException ("Need ProcDataWithParams");
}
ProcessLocal process = (ProcessLocal)act.containerLocal();
FormalParameter[] fps
= ((ProcessDataWithParams)result).formalParameters();
mergeResult (process, fps, result);
}
/**
* Return a string representation of this object.
* @return the representation
*/
public String toString() {
return "Process[" + packageId() + "/" + processId() + "]";
}
/**
* Helper class for retrieving the tool based implementation from
* the process definition.
*/
public class SAXInitializer extends StackedHandler {
private List apList = new ArrayList ();
/**
* Receive notification of the beginning of an element.
*
* @param uri the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace processing is not
* being performed.
* @param loc the local name (without prefix), or the empty string
* if Namespace processing is not being performed.
* @param raw the raw XML 1.0 name (with prefix), or the empty
* string if raw names are not available.
* @param a the attributes attached to the element. If there are
* no attributes, it shall be an empty Attributes object.
* @throws SAXException not thrown.
*/
public void startElement
(String uri, String loc, String raw, Attributes a)
throws SAXException {
if (loc.equals ("SubFlow")) {
setContextData ("implementation", ProcBasedImpl.this);
packageId = (String)getContextData ("packageId");
processId = a.getValue ("Id");
if (logger.isDebugEnabled ()) {
logger.debug
("Creating sub flow invocation for: "
+ packageId + "/" + processId);
}
String em = a.getValue ("Execution");
if (em != null && em.equals ("ASYNCHR")) {
execMode = ASYNCHR;
}
}
}
/**
* Receive notification of the end of an element.
*
* @param uri the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace processing is not
* being performed.
* @param loc the local name (without prefix), or the empty string
* if Namespace processing is not being performed.
* @param raw the raw XML 1.0 name (with prefix), or the empty
* string if raw names are not available.
* @throws SAXException not thrown.
*/
public void endElement(String uri, String loc, String raw)
throws SAXException {
if (loc.equals ("ActualParameter")) {
apList.add (text().trim());
} else if (loc.equals ("SubFlow")) {
actualParams = (String[])
apList.toArray(new String[apList.size()]);
}
}
}
/**
* Return a handler that can be used to initialize an object
* from SAX events. The instance created is returned in the
* HandlerStack's context data as "implementation".
* @return the handler.
*/
public static StackedHandler saxConstructor () {
return (new ProcBasedImpl()).newSaxInitializer ();
}
private StackedHandler newSaxInitializer () {
return new SAXInitializer ();
}
}