0001: /*
0002: * Copyright 2004-2007 the original author or authors.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package org.springframework.webflow.engine.builder.xml;
0017:
0018: import java.io.IOException;
0019: import java.util.Arrays;
0020: import java.util.Collections;
0021: import java.util.Iterator;
0022: import java.util.LinkedList;
0023: import java.util.List;
0024:
0025: import javax.xml.parsers.ParserConfigurationException;
0026:
0027: import org.springframework.beans.factory.BeanFactory;
0028: import org.springframework.beans.factory.config.ConfigurableBeanFactory;
0029: import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
0030: import org.springframework.binding.convert.ConversionExecutor;
0031: import org.springframework.binding.convert.ConversionService;
0032: import org.springframework.binding.expression.Expression;
0033: import org.springframework.binding.expression.ExpressionParser;
0034: import org.springframework.binding.expression.SettableExpression;
0035: import org.springframework.binding.expression.support.CollectionAddingExpression;
0036: import org.springframework.binding.mapping.AttributeMapper;
0037: import org.springframework.binding.mapping.DefaultAttributeMapper;
0038: import org.springframework.binding.mapping.Mapping;
0039: import org.springframework.binding.mapping.RequiredMapping;
0040: import org.springframework.binding.method.MethodSignature;
0041: import org.springframework.binding.method.Parameter;
0042: import org.springframework.binding.method.Parameters;
0043: import org.springframework.context.ApplicationContext;
0044: import org.springframework.context.support.GenericApplicationContext;
0045: import org.springframework.core.io.Resource;
0046: import org.springframework.core.style.ToStringCreator;
0047: import org.springframework.util.Assert;
0048: import org.springframework.util.StringUtils;
0049: import org.springframework.util.xml.DomUtils;
0050: import org.springframework.web.context.WebApplicationContext;
0051: import org.springframework.web.context.support.GenericWebApplicationContext;
0052: import org.springframework.webflow.action.ActionResultExposer;
0053: import org.springframework.webflow.action.EvaluateAction;
0054: import org.springframework.webflow.action.SetAction;
0055: import org.springframework.webflow.core.collection.AttributeMap;
0056: import org.springframework.webflow.core.collection.LocalAttributeMap;
0057: import org.springframework.webflow.core.collection.MutableAttributeMap;
0058: import org.springframework.webflow.engine.AnnotatedAction;
0059: import org.springframework.webflow.engine.Flow;
0060: import org.springframework.webflow.engine.FlowAttributeMapper;
0061: import org.springframework.webflow.engine.FlowExecutionExceptionHandler;
0062: import org.springframework.webflow.engine.FlowVariable;
0063: import org.springframework.webflow.engine.TargetStateResolver;
0064: import org.springframework.webflow.engine.Transition;
0065: import org.springframework.webflow.engine.TransitionCriteria;
0066: import org.springframework.webflow.engine.ViewSelector;
0067: import org.springframework.webflow.engine.builder.BaseFlowBuilder;
0068: import org.springframework.webflow.engine.builder.FlowArtifactFactory;
0069: import org.springframework.webflow.engine.builder.FlowBuilderException;
0070: import org.springframework.webflow.engine.builder.FlowServiceLocator;
0071: import org.springframework.webflow.engine.support.AttributeExpression;
0072: import org.springframework.webflow.engine.support.BeanFactoryFlowVariable;
0073: import org.springframework.webflow.engine.support.BooleanExpressionTransitionCriteria;
0074: import org.springframework.webflow.engine.support.SimpleFlowVariable;
0075: import org.springframework.webflow.engine.support.TransitionCriteriaChain;
0076: import org.springframework.webflow.engine.support.TransitionExecutingStateExceptionHandler;
0077: import org.springframework.webflow.execution.Action;
0078: import org.springframework.webflow.execution.ScopeType;
0079: import org.springframework.webflow.util.ResourceHolder;
0080: import org.w3c.dom.Document;
0081: import org.w3c.dom.Element;
0082: import org.w3c.dom.Node;
0083: import org.w3c.dom.NodeList;
0084: import org.xml.sax.SAXException;
0085:
0086: /**
0087: * Flow builder that builds flows as defined in an XML document. The XML document should adhere to the following format:
0088: *
0089: * <pre>
0090: * <?xml version="1.0" encoding="UTF-8"?>
0091: * <flow xmlns="http://www.springframework.org/schema/webflow"
0092: * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
0093: * xsi:schemaLocation="http://www.springframework.org/schema/webflow
0094: * http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd">
0095: *
0096: * <!-- Define your states here -->
0097: *
0098: * </flow>
0099: * </pre>
0100: *
0101: * <p>
0102: * Consult the <a href="http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd">web flow XML schema</a>
0103: * for more information on the XML-based flow definition format.
0104: * <p>
0105: * This builder will setup a flow-local bean factory for the flow being constructed. That flow-local bean factory will
0106: * be populated with XML bean definitions contained in files referenced using the "import" element. The flow-local bean
0107: * factory will use the bean factory defing this flow builder as a parent. As such, the flow can access artifacts in
0108: * either its flow-local bean factory or in the parent bean factory hierarchy, e.g. the bean factory of the dispatcher.
0109: *
0110: * @author Erwin Vervaet
0111: * @author Keith Donald
0112: */
0113: public class XmlFlowBuilder extends BaseFlowBuilder implements
0114: ResourceHolder {
0115:
0116: // recognized XML elements and attributes
0117:
0118: private static final String ID_ATTRIBUTE = "id";
0119:
0120: private static final String IDREF_ATTRIBUTE = "idref";
0121:
0122: private static final String BEAN_ATTRIBUTE = "bean";
0123:
0124: private static final String FLOW_ELEMENT = "flow";
0125:
0126: private static final String START_STATE_ELEMENT = "start-state";
0127:
0128: private static final String ACTION_STATE_ELEMENT = "action-state";
0129:
0130: private static final String ACTION_ELEMENT = "action";
0131:
0132: private static final String NAME_ATTRIBUTE = "name";
0133:
0134: private static final String METHOD_ATTRIBUTE = "method";
0135:
0136: private static final String BEAN_ACTION_ELEMENT = "bean-action";
0137:
0138: private static final String METHOD_ARGUMENTS_ELEMENT = "method-arguments";
0139:
0140: private static final String ARGUMENT_ELEMENT = "argument";
0141:
0142: private static final String EXPRESSION_ATTRIBUTE = "expression";
0143:
0144: private static final String PARAMETER_TYPE_ATTRIBUTE = "parameter-type";
0145:
0146: private static final String METHOD_RESULT_ELEMENT = "method-result";
0147:
0148: private static final String EVALUATE_ACTION_ELEMENT = "evaluate-action";
0149:
0150: private static final String SET_ELEMENT = "set";
0151:
0152: private static final String ATTRIBUTE_ATTRIBUTE = "attribute";
0153:
0154: private static final String EVALUATION_RESULT_ELEMENT = "evaluation-result";
0155:
0156: private static final String DEFAULT_VALUE = "default";
0157:
0158: private static final String VIEW_STATE_ELEMENT = "view-state";
0159:
0160: private static final String VIEW_ATTRIBUTE = "view";
0161:
0162: private static final String DECISION_STATE_ELEMENT = "decision-state";
0163:
0164: private static final String IF_ELEMENT = "if";
0165:
0166: private static final String TEST_ATTRIBUTE = "test";
0167:
0168: private static final String THEN_ATTRIBUTE = "then";
0169:
0170: private static final String ELSE_ATTRIBUTE = "else";
0171:
0172: private static final String SUBFLOW_STATE_ELEMENT = "subflow-state";
0173:
0174: private static final String FLOW_ATTRIBUTE = "flow";
0175:
0176: private static final String ATTRIBUTE_MAPPER_ELEMENT = "attribute-mapper";
0177:
0178: private static final String OUTPUT_MAPPER_ELEMENT = "output-mapper";
0179:
0180: private static final String OUTPUT_ATTRIBUTE_ELEMENT = "output-attribute";
0181:
0182: private static final String INPUT_MAPPER_ELEMENT = "input-mapper";
0183:
0184: private static final String INPUT_ATTRIBUTE_ELEMENT = "input-attribute";
0185:
0186: private static final String MAPPING_ELEMENT = "mapping";
0187:
0188: private static final String SOURCE_ATTRIBUTE = "source";
0189:
0190: private static final String TARGET_ATTRIBUTE = "target";
0191:
0192: private static final String FROM_ATTRIBUTE = "from";
0193:
0194: private static final String TO_ATTRIBUTE = "to";
0195:
0196: private static final String REQUIRED_ATTRIBUTE = "required";
0197:
0198: private static final String TARGET_COLLECTION_ATTRIBUTE = "target-collection";
0199:
0200: private static final String END_STATE_ELEMENT = "end-state";
0201:
0202: private static final String TRANSITION_ELEMENT = "transition";
0203:
0204: private static final String GLOBAL_TRANSITIONS_ELEMENT = "global-transitions";
0205:
0206: private static final String ON_ATTRIBUTE = "on";
0207:
0208: private static final String ON_EXCEPTION_ATTRIBUTE = "on-exception";
0209:
0210: private static final String ATTRIBUTE_ELEMENT = "attribute";
0211:
0212: private static final String TYPE_ATTRIBUTE = "type";
0213:
0214: private static final String VALUE_ELEMENT = "value";
0215:
0216: private static final String VALUE_ATTRIBUTE = "value";
0217:
0218: private static final String VAR_ELEMENT = "var";
0219:
0220: private static final String SCOPE_ATTRIBUTE = "scope";
0221:
0222: private static final String CLASS_ATTRIBUTE = "class";
0223:
0224: private static final String START_ACTIONS_ELEMENT = "start-actions";
0225:
0226: private static final String END_ACTIONS_ELEMENT = "end-actions";
0227:
0228: private static final String ENTRY_ACTIONS_ELEMENT = "entry-actions";
0229:
0230: private static final String RENDER_ACTIONS_ELEMENT = "render-actions";
0231:
0232: private static final String EXIT_ACTIONS_ELEMENT = "exit-actions";
0233:
0234: private static final String EXCEPTION_HANDLER_ELEMENT = "exception-handler";
0235:
0236: private static final String INLINE_FLOW_ELEMENT = "inline-flow";
0237:
0238: private static final String IMPORT_ELEMENT = "import";
0239:
0240: private static final String RESOURCE_ATTRIBUTE = "resource";
0241:
0242: /**
0243: * The resource from which the document element being parsed was read. Used as a location for relative resource
0244: * lookup.
0245: */
0246: protected Resource location;
0247:
0248: /**
0249: * A flow service locator local to this builder that first looks in a locally-managed Spring bean factory for
0250: * services before searching the externally managed {@link #getFlowServiceLocator() service locator}.
0251: */
0252: private LocalFlowServiceLocator localFlowServiceLocator;
0253:
0254: /**
0255: * The loader for loading the flow definition resource XML document.
0256: */
0257: private DocumentLoader documentLoader = new DefaultDocumentLoader();
0258:
0259: /**
0260: * The in-memory document object model (DOM) of the XML Document read from the flow definition resource.
0261: */
0262: private Document document;
0263:
0264: /**
0265: * Create a new XML flow builder parsing the document at the specified location.
0266: * @param location the location of the XML-based flow definition resource
0267: */
0268: public XmlFlowBuilder(Resource location) {
0269: setLocation(location);
0270: }
0271:
0272: /**
0273: * Create a new XML flow builder parsing the document at the specified location, using the provided service locator
0274: * to access externally managed flow artifacts.
0275: * @param location the location of the XML-based flow definition resource
0276: * @param flowServiceLocator the locator for services needed by this builder to build its Flow
0277: */
0278: public XmlFlowBuilder(Resource location,
0279: FlowServiceLocator flowServiceLocator) {
0280: super (flowServiceLocator);
0281: setLocation(location);
0282: }
0283:
0284: /**
0285: * Returns the resource from which the document element was loaded. This is used for location relative loading of
0286: * other resources.
0287: */
0288: public Resource getLocation() {
0289: return location;
0290: }
0291:
0292: /**
0293: * Sets the resource from which the document element was loaded. This is used for location relative loading of other
0294: * resources.
0295: */
0296: public void setLocation(Resource location) {
0297: Assert
0298: .notNull(location,
0299: "The resource location of the XML-based flow definition is required");
0300: this .location = location;
0301: }
0302:
0303: /**
0304: * Sets the loader that will load the XML-based flow definition document. Optional, defaults to
0305: * {@link DefaultDocumentLoader}.
0306: * @param documentLoader the document loader
0307: */
0308: public void setDocumentLoader(DocumentLoader documentLoader) {
0309: Assert.notNull(documentLoader,
0310: "The XML document loader is required");
0311: this .documentLoader = documentLoader;
0312: }
0313:
0314: public String toString() {
0315: return new ToStringCreator(this ).append("location", location)
0316: .toString();
0317: }
0318:
0319: // implementing FlowBuilder
0320:
0321: public void init(String id, AttributeMap attributes)
0322: throws FlowBuilderException {
0323: localFlowServiceLocator = new LocalFlowServiceLocator(
0324: getFlowServiceLocator());
0325: try {
0326: document = documentLoader.loadDocument(location);
0327: } catch (IOException e) {
0328: throw new FlowBuilderException(
0329: "Could not access the XML flow definition resource at "
0330: + location, e);
0331: } catch (ParserConfigurationException e) {
0332: throw new FlowBuilderException(
0333: "Could not configure the parser to parse the XML flow definition at "
0334: + location, e);
0335: } catch (SAXException e) {
0336: throw new FlowBuilderException(
0337: "Could not parse the XML flow definition document at "
0338: + location, e);
0339: }
0340: setFlow(parseFlow(id, attributes, getDocumentElement()));
0341: }
0342:
0343: public void buildVariables() throws FlowBuilderException {
0344: parseAndAddFlowVariables(getDocumentElement(), getFlow());
0345: }
0346:
0347: public void buildInputMapper() throws FlowBuilderException {
0348: getFlow()
0349: .setInputMapper(parseInputMapper(getDocumentElement()));
0350: }
0351:
0352: public void buildStartActions() throws FlowBuilderException {
0353: parseAndAddStartActions(getDocumentElement(), getFlow());
0354: }
0355:
0356: public void buildInlineFlows() throws FlowBuilderException {
0357: parseAndAddInlineFlowDefinitions(getDocumentElement(),
0358: getFlow());
0359: }
0360:
0361: public void buildStates() throws FlowBuilderException {
0362: parseAndAddStateDefinitions(getDocumentElement(), getFlow());
0363: }
0364:
0365: public void buildGlobalTransitions() throws FlowBuilderException {
0366: parseAndAddGlobalTransitions(getDocumentElement(), getFlow());
0367: }
0368:
0369: public void buildEndActions() throws FlowBuilderException {
0370: parseAndAddEndActions(getDocumentElement(), getFlow());
0371: }
0372:
0373: public void buildOutputMapper() throws FlowBuilderException {
0374: getFlow().setOutputMapper(
0375: parseOutputMapper(getDocumentElement()));
0376: }
0377:
0378: public void buildExceptionHandlers() throws FlowBuilderException {
0379: getFlow().getExceptionHandlerSet().addAll(
0380: parseExceptionHandlers(getDocumentElement()));
0381: }
0382:
0383: public void dispose() {
0384: super .dispose();
0385: localFlowServiceLocator.diposeOfAnyRegistries();
0386: document = null;
0387: }
0388:
0389: // implementing ResourceHolder
0390:
0391: public Resource getResource() {
0392: return location;
0393: }
0394:
0395: // helpers
0396:
0397: /**
0398: * Returns the DOM document parsed from the XML file.
0399: */
0400: protected Document getDocument() {
0401: return document;
0402: }
0403:
0404: /**
0405: * Returns the root document element.
0406: */
0407: protected Element getDocumentElement() {
0408: return document.getDocumentElement();
0409: }
0410:
0411: /**
0412: * Returns the flow service locator local to this builder.
0413: */
0414: protected FlowServiceLocator getLocalFlowServiceLocator() {
0415: return localFlowServiceLocator;
0416: }
0417:
0418: /**
0419: * Returns the artifact factory of the flow service locator local to this builder.
0420: */
0421: protected FlowArtifactFactory getFlowArtifactFactory() {
0422: return getLocalFlowServiceLocator().getFlowArtifactFactory();
0423: }
0424:
0425: // utility (from Spring 2.x DomUtils)
0426:
0427: /**
0428: * Utility method that returns the first child element identified by its name.
0429: * @param ele the DOM element to analyze
0430: * @param childEleName the child element name to look for
0431: * @return the <code>org.w3c.dom.Element</code> instance, or <code>null</code> if none found
0432: */
0433: protected Element getChildElementByTagName(Element ele,
0434: String childEleName) {
0435: NodeList nl = ele.getChildNodes();
0436: for (int i = 0; i < nl.getLength(); i++) {
0437: Node node = nl.item(i);
0438: if (node instanceof Element
0439: && nodeNameEquals(node, childEleName)) {
0440: return (Element) node;
0441: }
0442: }
0443: return null;
0444: }
0445:
0446: /**
0447: * Namespace-aware equals comparison. Returns <code>true</code> if either {@link Node#getLocalName} or
0448: * {@link Node#getNodeName} equals <code>desiredName</code>, otherwise returns <code>false</code>.
0449: */
0450: protected boolean nodeNameEquals(Node node, String desiredName) {
0451: return desiredName.equals(node.getNodeName())
0452: || desiredName.equals(node.getLocalName());
0453: }
0454:
0455: // internal parsing logic and hook methods
0456:
0457: private Flow parseFlow(String id, AttributeMap attributes,
0458: Element flowElement) {
0459: if (!isFlowElement(flowElement)) {
0460: throw new IllegalStateException("This is not the '"
0461: + FLOW_ELEMENT + "' element");
0462: }
0463: Flow flow = getFlowArtifactFactory().createFlow(id,
0464: parseAttributes(flowElement).union(attributes));
0465: initLocalServiceRegistry(flowElement, flow);
0466: return flow;
0467: }
0468:
0469: private boolean isFlowElement(Element flowElement) {
0470: return nodeNameEquals(flowElement, FLOW_ELEMENT);
0471: }
0472:
0473: private void initLocalServiceRegistry(Element flowElement, Flow flow) {
0474: List importElements = DomUtils.getChildElementsByTagName(
0475: flowElement, IMPORT_ELEMENT);
0476: Resource[] resources = new Resource[importElements.size()];
0477: for (int i = 0; i < importElements.size(); i++) {
0478: Element importElement = (Element) importElements.get(i);
0479: try {
0480: resources[i] = getLocation().createRelative(
0481: importElement.getAttribute(RESOURCE_ATTRIBUTE));
0482: } catch (IOException e) {
0483: throw new FlowBuilderException(
0484: "Could not access flow-relative artifact resource '"
0485: + importElement
0486: .getAttribute(RESOURCE_ATTRIBUTE)
0487: + "'", e);
0488: }
0489: }
0490: localFlowServiceLocator.push(new LocalFlowServiceRegistry(flow,
0491: createLocalBeanFactory(flow, resources)));
0492: }
0493:
0494: /**
0495: * Create a bean factory serving as a local flow service registry.
0496: * @param flow the current flow definition being built
0497: * @param resources the file resources to assemble the bean factory from; typically XML-based
0498: * @return the bean factory
0499: * @since 1.0.4
0500: */
0501: protected BeanFactory createLocalBeanFactory(Flow flow,
0502: Resource[] resources) {
0503: // see if this factory has a parent
0504: BeanFactory parent = null;
0505: if (localFlowServiceLocator.isEmpty()) {
0506: try {
0507: parent = getFlowServiceLocator().getBeanFactory();
0508: } catch (UnsupportedOperationException e) {
0509: // can't link to a parent
0510: }
0511: } else {
0512: parent = localFlowServiceLocator.top().getBeanFactory();
0513: }
0514: // determine the context implementation based on the current environment
0515: GenericApplicationContext context;
0516: if (parent instanceof WebApplicationContext) {
0517: GenericWebApplicationContext webContext = new GenericWebApplicationContext();
0518: webContext
0519: .setServletContext(((WebApplicationContext) parent)
0520: .getServletContext());
0521: context = webContext;
0522: } else {
0523: context = new GenericApplicationContext();
0524: }
0525: // set the parent if necessary
0526: if (parent instanceof ApplicationContext) {
0527: context.setParent((ApplicationContext) parent);
0528: } else {
0529: if (parent != null) {
0530: context.getBeanFactory().setParentBeanFactory(parent);
0531: }
0532: }
0533: context.setResourceLoader(getFlowServiceLocator()
0534: .getResourceLoader());
0535: new XmlBeanDefinitionReader(context)
0536: .loadBeanDefinitions(resources);
0537: registerLocalBeans(flow, context
0538: .getDefaultListableBeanFactory());
0539: context.refresh();
0540: return context;
0541: }
0542:
0543: /**
0544: * Register beans in the bean factory local to the flow definition being built.
0545: * <p>
0546: * Subclasses may override this metod to customize the population of the bean factory local to
0547: * the flow definition being built; for example, to register mock implementations of services in a test environment.
0548: * @param flow the current flow definition being built
0549: * @param beanFactory the bean factory; register local beans with it using
0550: * {@link ConfigurableBeanFactory#registerSingleton(String, Object)}
0551: * @since 1.0.4
0552: */
0553: protected void registerLocalBeans(Flow flow,
0554: ConfigurableBeanFactory beanFactory) {
0555: }
0556:
0557: private void destroyLocalServiceRegistry() {
0558: localFlowServiceLocator.pop();
0559: }
0560:
0561: private void parseAndAddFlowVariables(Element flowElement, Flow flow) {
0562: List varElements = DomUtils.getChildElementsByTagName(
0563: flowElement, VAR_ELEMENT);
0564: for (Iterator it = varElements.iterator(); it.hasNext();) {
0565: flow.addVariable(parseVariable((Element) it.next()));
0566: }
0567: }
0568:
0569: private FlowVariable parseVariable(Element element) {
0570: ScopeType scope = parseScope(element, ScopeType.FLOW);
0571: if (StringUtils.hasText(element.getAttribute(BEAN_ATTRIBUTE))) {
0572: BeanFactory beanFactory = getLocalFlowServiceLocator()
0573: .getBeanFactory();
0574: return new BeanFactoryFlowVariable(element
0575: .getAttribute(NAME_ATTRIBUTE), element
0576: .getAttribute(BEAN_ATTRIBUTE), beanFactory, scope);
0577: } else {
0578: if (StringUtils.hasText(element
0579: .getAttribute(CLASS_ATTRIBUTE))) {
0580: Class variableClass = (Class) fromStringTo(Class.class)
0581: .execute(element.getAttribute(CLASS_ATTRIBUTE));
0582: return new SimpleFlowVariable(element
0583: .getAttribute(NAME_ATTRIBUTE), variableClass,
0584: scope);
0585: } else {
0586: BeanFactory beanFactory = getLocalFlowServiceLocator()
0587: .getBeanFactory();
0588: return new BeanFactoryFlowVariable(element
0589: .getAttribute(NAME_ATTRIBUTE), null,
0590: beanFactory, scope);
0591: }
0592: }
0593: }
0594:
0595: private void parseAndAddStartActions(Element element, Flow flow) {
0596: Element startElement = getChildElementByTagName(element,
0597: START_ACTIONS_ELEMENT);
0598: if (startElement != null) {
0599: flow.getStartActionList().addAll(
0600: parseAnnotatedActions(startElement));
0601: }
0602: }
0603:
0604: private void parseAndAddEndActions(Element element, Flow flow) {
0605: Element endElement = getChildElementByTagName(element,
0606: END_ACTIONS_ELEMENT);
0607: if (endElement != null) {
0608: flow.getEndActionList().addAll(
0609: parseAnnotatedActions(endElement));
0610: }
0611: }
0612:
0613: private void parseAndAddGlobalTransitions(Element element, Flow flow) {
0614: Element globalTransitionsElement = getChildElementByTagName(
0615: element, GLOBAL_TRANSITIONS_ELEMENT);
0616: if (globalTransitionsElement != null) {
0617: flow.getGlobalTransitionSet().addAll(
0618: parseTransitions(globalTransitionsElement));
0619: }
0620: }
0621:
0622: private void parseAndAddInlineFlowDefinitions(
0623: Element parentFlowElement, Flow flow) {
0624: List inlineFlowElements = DomUtils.getChildElementsByTagName(
0625: parentFlowElement, INLINE_FLOW_ELEMENT);
0626: for (Iterator it = inlineFlowElements.iterator(); it.hasNext();) {
0627: Element inlineFlowElement = (Element) it.next();
0628: String inlineFlowId = inlineFlowElement
0629: .getAttribute(ID_ATTRIBUTE);
0630: Element flowElement = getChildElementByTagName(
0631: inlineFlowElement, FLOW_ATTRIBUTE);
0632: Flow inlineFlow = parseFlow(inlineFlowId, null, flowElement);
0633: buildInlineFlow(flowElement, inlineFlow);
0634: flow.addInlineFlow(inlineFlow);
0635: }
0636: }
0637:
0638: private void buildInlineFlow(Element flowElement, Flow inlineFlow) {
0639: parseAndAddFlowVariables(flowElement, inlineFlow);
0640: inlineFlow.setInputMapper(parseInputMapper(flowElement));
0641: parseAndAddStartActions(flowElement, inlineFlow);
0642: parseAndAddInlineFlowDefinitions(flowElement, inlineFlow);
0643: parseAndAddStateDefinitions(flowElement, inlineFlow);
0644: parseAndAddGlobalTransitions(flowElement, inlineFlow);
0645: parseAndAddEndActions(flowElement, inlineFlow);
0646: inlineFlow.setOutputMapper(parseOutputMapper(flowElement));
0647: inlineFlow.getExceptionHandlerSet().addAll(
0648: parseExceptionHandlers(flowElement));
0649:
0650: destroyLocalServiceRegistry();
0651: }
0652:
0653: private void parseAndAddStateDefinitions(Element flowElement,
0654: Flow flow) {
0655: NodeList childNodeList = flowElement.getChildNodes();
0656: for (int i = 0; i < childNodeList.getLength(); i++) {
0657: Node childNode = childNodeList.item(i);
0658: if (childNode instanceof Element) {
0659: Element stateElement = (Element) childNode;
0660: if (nodeNameEquals(stateElement, ACTION_STATE_ELEMENT)) {
0661: parseAndAddActionState(stateElement, flow);
0662: } else if (nodeNameEquals(stateElement,
0663: VIEW_STATE_ELEMENT)) {
0664: parseAndAddViewState(stateElement, flow);
0665: } else if (nodeNameEquals(stateElement,
0666: DECISION_STATE_ELEMENT)) {
0667: parseAndAddDecisionState(stateElement, flow);
0668: } else if (nodeNameEquals(stateElement,
0669: SUBFLOW_STATE_ELEMENT)) {
0670: parseAndAddSubflowState(stateElement, flow);
0671: } else if (nodeNameEquals(stateElement,
0672: END_STATE_ELEMENT)) {
0673: parseAndAddEndState(stateElement, flow);
0674: }
0675: }
0676: }
0677: parseAndSetStartState(flowElement, flow);
0678: }
0679:
0680: private void parseAndSetStartState(Element element, Flow flow) {
0681: String startStateId = getStartStateId(element);
0682: flow.setStartState(startStateId);
0683: }
0684:
0685: private String getStartStateId(Element element) {
0686: Element startStateElement = getChildElementByTagName(element,
0687: START_STATE_ELEMENT);
0688: return startStateElement.getAttribute(IDREF_ATTRIBUTE);
0689: }
0690:
0691: private void parseAndAddActionState(Element element, Flow flow) {
0692: getFlowArtifactFactory().createActionState(parseId(element),
0693: flow, parseEntryActions(element),
0694: parseAnnotatedActions(element),
0695: parseTransitions(element),
0696: parseExceptionHandlers(element),
0697: parseExitActions(element), parseAttributes(element));
0698: }
0699:
0700: private void parseAndAddViewState(Element element, Flow flow) {
0701: getFlowArtifactFactory().createViewState(parseId(element),
0702: flow, parseEntryActions(element),
0703: parseViewSelector(element),
0704: parseRenderActions(element), parseTransitions(element),
0705: parseExceptionHandlers(element),
0706: parseExitActions(element), parseAttributes(element));
0707: }
0708:
0709: private void parseAndAddDecisionState(Element element, Flow flow) {
0710: getFlowArtifactFactory().createDecisionState(parseId(element),
0711: flow, parseEntryActions(element), parseIfs(element),
0712: parseExceptionHandlers(element),
0713: parseExitActions(element), parseAttributes(element));
0714: }
0715:
0716: private void parseAndAddSubflowState(Element element, Flow flow) {
0717: getFlowArtifactFactory().createSubflowState(parseId(element),
0718: flow, parseEntryActions(element),
0719: parseSubflow(element),
0720: parseFlowAttributeMapper(element),
0721: parseTransitions(element),
0722: parseExceptionHandlers(element),
0723: parseExitActions(element), parseAttributes(element));
0724: }
0725:
0726: private void parseAndAddEndState(Element element, Flow flow) {
0727: getFlowArtifactFactory().createEndState(parseId(element), flow,
0728: parseEntryActions(element), parseViewSelector(element),
0729: parseOutputMapper(element),
0730: parseExceptionHandlers(element),
0731: parseAttributes(element));
0732: }
0733:
0734: private String parseId(Element element) {
0735: return element.getAttribute(ID_ATTRIBUTE);
0736: }
0737:
0738: private Action[] parseEntryActions(Element element) {
0739: Element entryActionsElement = getChildElementByTagName(element,
0740: ENTRY_ACTIONS_ELEMENT);
0741: if (entryActionsElement != null) {
0742: return parseAnnotatedActions(entryActionsElement);
0743: } else {
0744: return null;
0745: }
0746: }
0747:
0748: private Action[] parseRenderActions(Element element) {
0749: Element renderActionsElement = getChildElementByTagName(
0750: element, RENDER_ACTIONS_ELEMENT);
0751: if (renderActionsElement != null) {
0752: return parseAnnotatedActions(renderActionsElement);
0753: } else {
0754: return null;
0755: }
0756: }
0757:
0758: private Action[] parseExitActions(Element element) {
0759: Element exitActionsElement = getChildElementByTagName(element,
0760: EXIT_ACTIONS_ELEMENT);
0761: if (exitActionsElement != null) {
0762: return parseAnnotatedActions(exitActionsElement);
0763: } else {
0764: return null;
0765: }
0766: }
0767:
0768: private Transition[] parseTransitions(Element element) {
0769: List transitions = new LinkedList();
0770: List transitionElements = DomUtils.getChildElementsByTagName(
0771: element, TRANSITION_ELEMENT);
0772: for (Iterator it = transitionElements.iterator(); it.hasNext();) {
0773: Element transitionElement = (Element) it.next();
0774: if (!StringUtils.hasText(transitionElement
0775: .getAttribute(ON_EXCEPTION_ATTRIBUTE))) {
0776: // the "on-exception transition" is not really a transition but rather
0777: // a FlowExecutionExceptionHandler (see parseTransitionExecutingExceptionHandlers)
0778: transitions.add(parseTransition(transitionElement));
0779: }
0780: }
0781: return (Transition[]) transitions
0782: .toArray(new Transition[transitions.size()]);
0783: }
0784:
0785: private Transition parseTransition(Element element) {
0786: TransitionCriteria matchingCriteria = (TransitionCriteria) fromStringTo(
0787: TransitionCriteria.class).execute(
0788: element.getAttribute(ON_ATTRIBUTE));
0789: TargetStateResolver targetStateResolver = (TargetStateResolver) fromStringTo(
0790: TargetStateResolver.class).execute(
0791: element.getAttribute(TO_ATTRIBUTE));
0792: TransitionCriteria executionCriteria = TransitionCriteriaChain
0793: .criteriaChainFor(parseAnnotatedActions(element));
0794: return getFlowArtifactFactory().createTransition(
0795: targetStateResolver, matchingCriteria,
0796: executionCriteria, parseAttributes(element));
0797: }
0798:
0799: private ViewSelector parseViewSelector(Element element) {
0800: String viewName = element.getAttribute(VIEW_ATTRIBUTE);
0801: return (ViewSelector) fromStringTo(ViewSelector.class).execute(
0802: viewName);
0803: }
0804:
0805: private Flow parseSubflow(Element element) {
0806: return getLocalFlowServiceLocator().getSubflow(
0807: element.getAttribute(FLOW_ATTRIBUTE));
0808: }
0809:
0810: private AnnotatedAction[] parseAnnotatedActions(Element element) {
0811: List actions = new LinkedList();
0812: NodeList childNodeList = element.getChildNodes();
0813: for (int i = 0; i < childNodeList.getLength(); i++) {
0814: Node childNode = childNodeList.item(i);
0815: if (!(childNode instanceof Element)) {
0816: continue;
0817: }
0818:
0819: if (nodeNameEquals(childNode, ACTION_ELEMENT)) {
0820: // parse standard action
0821: actions.add(parseAnnotatedAction((Element) childNode));
0822: } else if (nodeNameEquals(childNode, BEAN_ACTION_ELEMENT)) {
0823: // parse bean invoking action
0824: actions
0825: .add(parseAnnotatedBeanInvokingAction((Element) childNode));
0826: } else if (nodeNameEquals(childNode,
0827: EVALUATE_ACTION_ELEMENT)) {
0828: // parse evaluate action
0829: actions
0830: .add(parseAnnotatedEvaluateAction((Element) childNode));
0831: } else if (nodeNameEquals(childNode, SET_ELEMENT)) {
0832: // parse set action
0833: actions
0834: .add(parseAnnotatedSetAction((Element) childNode));
0835: }
0836: }
0837: return (AnnotatedAction[]) actions
0838: .toArray(new AnnotatedAction[actions.size()]);
0839: }
0840:
0841: private AnnotatedAction parseAnnotatedAction(Element element) {
0842: AnnotatedAction annotated = new AnnotatedAction(
0843: parseAction(element));
0844: parseCommonProperties(element, annotated);
0845: if (element.hasAttribute(METHOD_ATTRIBUTE)) {
0846: annotated.setMethod(element.getAttribute(METHOD_ATTRIBUTE));
0847: }
0848: return annotated;
0849: }
0850:
0851: private Action parseAction(Element element) {
0852: String actionId = element.getAttribute(BEAN_ATTRIBUTE);
0853: return getLocalFlowServiceLocator().getAction(actionId);
0854: }
0855:
0856: private AnnotatedAction parseCommonProperties(Element element,
0857: AnnotatedAction annotated) {
0858: if (element.hasAttribute(NAME_ATTRIBUTE)) {
0859: annotated.setName(element.getAttribute(NAME_ATTRIBUTE));
0860: }
0861: annotated.getAttributeMap().putAll(parseAttributes(element));
0862: return annotated;
0863: }
0864:
0865: private AnnotatedAction parseAnnotatedBeanInvokingAction(
0866: Element element) {
0867: AnnotatedAction annotated = new AnnotatedAction(
0868: parseBeanInvokingAction(element));
0869: return parseCommonProperties(element, annotated);
0870: }
0871:
0872: private Action parseBeanInvokingAction(Element element) {
0873: String beanId = element.getAttribute(BEAN_ATTRIBUTE);
0874: String methodName = element.getAttribute(METHOD_ATTRIBUTE);
0875: Parameters parameters = parseMethodParameters(element);
0876: MethodSignature methodSignature = new MethodSignature(
0877: methodName, parameters);
0878: ActionResultExposer resultExposer = parseMethodResultExposer(element);
0879: return getLocalFlowServiceLocator()
0880: .getBeanInvokingActionFactory()
0881: .createBeanInvokingAction(
0882: beanId,
0883: getLocalFlowServiceLocator().getBeanFactory(),
0884: methodSignature,
0885: resultExposer,
0886: getLocalFlowServiceLocator()
0887: .getConversionService(), null);
0888: }
0889:
0890: private Parameters parseMethodParameters(Element element) {
0891: Element methodArgumentsElement = getChildElementByTagName(
0892: element, METHOD_ARGUMENTS_ELEMENT);
0893: if (methodArgumentsElement == null) {
0894: return Parameters.NONE;
0895: }
0896: Parameters parameters = new Parameters();
0897: Iterator it = DomUtils.getChildElementsByTagName(
0898: methodArgumentsElement, ARGUMENT_ELEMENT).iterator();
0899: while (it.hasNext()) {
0900: Element argumentElement = (Element) it.next();
0901: Expression name = getLocalFlowServiceLocator()
0902: .getExpressionParser()
0903: .parseExpression(
0904: argumentElement
0905: .getAttribute(EXPRESSION_ATTRIBUTE));
0906: Class type = null;
0907: if (argumentElement.hasAttribute(PARAMETER_TYPE_ATTRIBUTE)) {
0908: type = (Class) fromStringTo(Class.class)
0909: .execute(
0910: argumentElement
0911: .getAttribute(PARAMETER_TYPE_ATTRIBUTE));
0912: }
0913: parameters.add(new Parameter(type, name));
0914: }
0915: return parameters;
0916: }
0917:
0918: private ActionResultExposer parseMethodResultExposer(Element element) {
0919: Element resultElement = getChildElementByTagName(element,
0920: METHOD_RESULT_ELEMENT);
0921: if (resultElement != null) {
0922: return parseActionResultExposer(resultElement);
0923: } else {
0924: return null;
0925: }
0926: }
0927:
0928: private ActionResultExposer parseActionResultExposer(Element element) {
0929: String resultName = element.getAttribute(NAME_ATTRIBUTE);
0930: return new ActionResultExposer(resultName, parseScope(element,
0931: ScopeType.REQUEST));
0932: }
0933:
0934: private AnnotatedAction parseAnnotatedEvaluateAction(Element element) {
0935: AnnotatedAction annotated = new AnnotatedAction(
0936: parseEvaluateAction(element));
0937: return parseCommonProperties(element, annotated);
0938: }
0939:
0940: private Action parseEvaluateAction(Element element) {
0941: String expressionString = element
0942: .getAttribute(EXPRESSION_ATTRIBUTE);
0943: Expression expression = getLocalFlowServiceLocator()
0944: .getExpressionParser()
0945: .parseExpression(expressionString);
0946: return new EvaluateAction(expression,
0947: parseEvaluationResultExposer(element));
0948: }
0949:
0950: private ActionResultExposer parseEvaluationResultExposer(
0951: Element element) {
0952: Element resultElement = getChildElementByTagName(element,
0953: EVALUATION_RESULT_ELEMENT);
0954: if (resultElement != null) {
0955: return parseActionResultExposer(resultElement);
0956: } else {
0957: return null;
0958: }
0959: }
0960:
0961: private AnnotatedAction parseAnnotatedSetAction(Element element) {
0962: AnnotatedAction annotated = new AnnotatedAction(
0963: parseSetAction(element));
0964: return parseCommonProperties(element, annotated);
0965: }
0966:
0967: private Action parseSetAction(Element element) {
0968: String attributeExpressionString = element
0969: .getAttribute(ATTRIBUTE_ATTRIBUTE);
0970: SettableExpression attributeExpression = getLocalFlowServiceLocator()
0971: .getExpressionParser().parseSettableExpression(
0972: attributeExpressionString);
0973: Expression valueExpression = getLocalFlowServiceLocator()
0974: .getExpressionParser().parseExpression(
0975: element.getAttribute(VALUE_ATTRIBUTE));
0976: return new SetAction(attributeExpression, parseScope(element,
0977: ScopeType.REQUEST), valueExpression);
0978: }
0979:
0980: private ScopeType parseScope(Element element, ScopeType defaultValue) {
0981: if (element.hasAttribute(SCOPE_ATTRIBUTE)
0982: && !element.getAttribute(SCOPE_ATTRIBUTE).equals(
0983: DEFAULT_VALUE)) {
0984: return (ScopeType) fromStringTo(ScopeType.class).execute(
0985: element.getAttribute(SCOPE_ATTRIBUTE));
0986: } else {
0987: return defaultValue;
0988: }
0989: }
0990:
0991: private AttributeMap parseAttributes(Element element) {
0992: LocalAttributeMap attributes = new LocalAttributeMap();
0993: List propertyElements = DomUtils.getChildElementsByTagName(
0994: element, ATTRIBUTE_ELEMENT);
0995: for (int i = 0; i < propertyElements.size(); i++) {
0996: parseAndSetAttribute((Element) propertyElements.get(i),
0997: attributes);
0998: }
0999: return attributes;
1000: }
1001:
1002: private void parseAndSetAttribute(Element element,
1003: MutableAttributeMap attributes) {
1004: String name = element.getAttribute(NAME_ATTRIBUTE);
1005: String value = null;
1006: if (element.hasAttribute(VALUE_ATTRIBUTE)) {
1007: value = element.getAttribute(VALUE_ATTRIBUTE);
1008: } else {
1009: List valueElements = DomUtils.getChildElementsByTagName(
1010: element, VALUE_ELEMENT);
1011: Assert.state(valueElements.size() == 1,
1012: "A property value should be specified for property '"
1013: + name + "'");
1014: value = DomUtils.getTextValue((Element) valueElements
1015: .get(0));
1016: }
1017: attributes.put(name, convertPropertyValue(element, value));
1018: }
1019:
1020: private Object convertPropertyValue(Element element,
1021: String stringValue) {
1022: if (element.hasAttribute(TYPE_ATTRIBUTE)) {
1023: Class targetClass = (Class) fromStringTo(Class.class)
1024: .execute(element.getAttribute(TYPE_ATTRIBUTE));
1025: // convert string value to instance of target class
1026: return fromStringTo(targetClass).execute(stringValue);
1027: } else {
1028: return stringValue;
1029: }
1030: }
1031:
1032: private Transition[] parseIfs(Element element) {
1033: List transitions = new LinkedList();
1034: List transitionElements = DomUtils.getChildElementsByTagName(
1035: element, IF_ELEMENT);
1036: for (Iterator it = transitionElements.iterator(); it.hasNext();) {
1037: transitions.addAll(Arrays.asList(parseIf((Element) it
1038: .next())));
1039: }
1040: return (Transition[]) transitions
1041: .toArray(new Transition[transitions.size()]);
1042: }
1043:
1044: private Transition[] parseIf(Element element) {
1045: Transition thenTransition = parseThen(element);
1046: if (StringUtils.hasText(element.getAttribute(ELSE_ATTRIBUTE))) {
1047: Transition elseTransition = parseElse(element);
1048: return new Transition[] { thenTransition, elseTransition };
1049: } else {
1050: return new Transition[] { thenTransition };
1051: }
1052: }
1053:
1054: private Transition parseThen(Element element) {
1055: Expression expression = getLocalFlowServiceLocator()
1056: .getExpressionParser().parseExpression(
1057: element.getAttribute(TEST_ATTRIBUTE));
1058: TransitionCriteria matchingCriteria = new BooleanExpressionTransitionCriteria(
1059: expression);
1060: TargetStateResolver targetStateResolver = (TargetStateResolver) fromStringTo(
1061: TargetStateResolver.class).execute(
1062: element.getAttribute(THEN_ATTRIBUTE));
1063: return getFlowArtifactFactory().createTransition(
1064: targetStateResolver, matchingCriteria, null, null);
1065: }
1066:
1067: private Transition parseElse(Element element) {
1068: TargetStateResolver targetStateResolver = (TargetStateResolver) fromStringTo(
1069: TargetStateResolver.class).execute(
1070: element.getAttribute(ELSE_ATTRIBUTE));
1071: return getFlowArtifactFactory().createTransition(
1072: targetStateResolver, null, null, null);
1073: }
1074:
1075: private FlowAttributeMapper parseFlowAttributeMapper(Element element) {
1076: Element mapperElement = getChildElementByTagName(element,
1077: ATTRIBUTE_MAPPER_ELEMENT);
1078: if (mapperElement == null) {
1079: return null;
1080: }
1081: if (StringUtils.hasText(mapperElement
1082: .getAttribute(BEAN_ATTRIBUTE))) {
1083: return getLocalFlowServiceLocator().getAttributeMapper(
1084: mapperElement.getAttribute(BEAN_ATTRIBUTE));
1085: } else {
1086: return new ImmutableFlowAttributeMapper(
1087: parseInputMapper(mapperElement),
1088: parseOutputMapper(mapperElement));
1089: }
1090: }
1091:
1092: private AttributeMapper parseInputMapper(Element element) {
1093: Element mapperElement = getChildElementByTagName(element,
1094: INPUT_MAPPER_ELEMENT);
1095: if (mapperElement != null) {
1096: DefaultAttributeMapper mapper = new DefaultAttributeMapper();
1097: parseSimpleAttributeMappings(mapper, DomUtils
1098: .getChildElementsByTagName(mapperElement,
1099: INPUT_ATTRIBUTE_ELEMENT));
1100: parseMappings(mapper, mapperElement);
1101: return mapper;
1102: } else {
1103: return null;
1104: }
1105: }
1106:
1107: private AttributeMapper parseOutputMapper(Element element) {
1108: Element mapperElement = getChildElementByTagName(element,
1109: OUTPUT_MAPPER_ELEMENT);
1110: if (mapperElement != null) {
1111: DefaultAttributeMapper mapper = new DefaultAttributeMapper();
1112: parseSimpleAttributeMappings(mapper, DomUtils
1113: .getChildElementsByTagName(mapperElement,
1114: OUTPUT_ATTRIBUTE_ELEMENT));
1115: parseMappings(mapper, mapperElement);
1116: return mapper;
1117: } else {
1118: return null;
1119: }
1120: }
1121:
1122: private void parseMappings(DefaultAttributeMapper mapper,
1123: Element element) {
1124: ExpressionParser parser = getLocalFlowServiceLocator()
1125: .getExpressionParser();
1126: List mappingElements = DomUtils.getChildElementsByTagName(
1127: element, MAPPING_ELEMENT);
1128: for (Iterator it = mappingElements.iterator(); it.hasNext();) {
1129: Element mappingElement = (Element) it.next();
1130: Expression source = parser.parseExpression(mappingElement
1131: .getAttribute(SOURCE_ATTRIBUTE));
1132: SettableExpression target = null;
1133: if (StringUtils.hasText(mappingElement
1134: .getAttribute(TARGET_ATTRIBUTE))) {
1135: target = parser.parseSettableExpression(mappingElement
1136: .getAttribute(TARGET_ATTRIBUTE));
1137: } else if (StringUtils.hasText(mappingElement
1138: .getAttribute(TARGET_COLLECTION_ATTRIBUTE))) {
1139: target = new CollectionAddingExpression(
1140: parser
1141: .parseSettableExpression(mappingElement
1142: .getAttribute(TARGET_COLLECTION_ATTRIBUTE)));
1143: }
1144: if (getRequired(mappingElement, false)) {
1145: mapper.addMapping(new RequiredMapping(source, target,
1146: parseTypeConverter(mappingElement)));
1147: } else {
1148: mapper.addMapping(new Mapping(source, target,
1149: parseTypeConverter(mappingElement)));
1150: }
1151: }
1152: }
1153:
1154: private void parseSimpleAttributeMappings(
1155: DefaultAttributeMapper mapper, List elements) {
1156: ExpressionParser parser = getLocalFlowServiceLocator()
1157: .getExpressionParser();
1158: for (Iterator it = elements.iterator(); it.hasNext();) {
1159: Element element = (Element) it.next();
1160: SettableExpression attribute = parser
1161: .parseSettableExpression(element
1162: .getAttribute(NAME_ATTRIBUTE));
1163: SettableExpression expression = new AttributeExpression(
1164: attribute, parseScope(element, ScopeType.FLOW));
1165: if (getRequired(element, false)) {
1166: mapper.addMapping(new RequiredMapping(expression,
1167: expression, null));
1168: } else {
1169: mapper.addMapping(new Mapping(expression, expression,
1170: null));
1171: }
1172: }
1173: }
1174:
1175: private boolean getRequired(Element element, boolean defaultValue) {
1176: if (StringUtils.hasText(element
1177: .getAttribute(REQUIRED_ATTRIBUTE))) {
1178: return ((Boolean) fromStringTo(Boolean.class).execute(
1179: element.getAttribute(REQUIRED_ATTRIBUTE)))
1180: .booleanValue();
1181: } else {
1182: return defaultValue;
1183: }
1184: }
1185:
1186: private ConversionExecutor parseTypeConverter(Element element) {
1187: String from = element.getAttribute(FROM_ATTRIBUTE);
1188: String to = element.getAttribute(TO_ATTRIBUTE);
1189: if (StringUtils.hasText(from)) {
1190: if (StringUtils.hasText(to)) {
1191: ConversionService service = getLocalFlowServiceLocator()
1192: .getConversionService();
1193: Class sourceClass = (Class) fromStringTo(Class.class)
1194: .execute(from);
1195: Class targetClass = (Class) fromStringTo(Class.class)
1196: .execute(to);
1197: return service.getConversionExecutor(sourceClass,
1198: targetClass);
1199: } else {
1200: throw new IllegalArgumentException(
1201: "Use of the 'from' attribute requires use of the 'to' attribute");
1202: }
1203: } else {
1204: Assert
1205: .isTrue(!StringUtils.hasText(to),
1206: "Use of the 'to' attribute requires use of the 'from' attribute");
1207: }
1208: return null;
1209: }
1210:
1211: private FlowExecutionExceptionHandler[] parseExceptionHandlers(
1212: Element element) {
1213: FlowExecutionExceptionHandler[] transitionExecutingHandlers = parseTransitionExecutingExceptionHandlers(element);
1214: FlowExecutionExceptionHandler[] customHandlers = parseCustomExceptionHandlers(element);
1215: FlowExecutionExceptionHandler[] exceptionHandlers = new FlowExecutionExceptionHandler[transitionExecutingHandlers.length
1216: + customHandlers.length];
1217: System.arraycopy(transitionExecutingHandlers, 0,
1218: exceptionHandlers, 0,
1219: transitionExecutingHandlers.length);
1220: System.arraycopy(customHandlers, 0, exceptionHandlers,
1221: transitionExecutingHandlers.length,
1222: customHandlers.length);
1223: return exceptionHandlers;
1224: }
1225:
1226: private FlowExecutionExceptionHandler[] parseTransitionExecutingExceptionHandlers(
1227: Element element) {
1228: List transitionElements = Collections.EMPTY_LIST;
1229: if (isFlowElement(element)) {
1230: Element globalTransitionsElement = getChildElementByTagName(
1231: element, GLOBAL_TRANSITIONS_ELEMENT);
1232: if (globalTransitionsElement != null) {
1233: transitionElements = DomUtils
1234: .getChildElementsByTagName(
1235: globalTransitionsElement,
1236: TRANSITION_ELEMENT);
1237: }
1238: } else {
1239: transitionElements = DomUtils.getChildElementsByTagName(
1240: element, TRANSITION_ELEMENT);
1241: }
1242: List exceptionHandlers = new LinkedList();
1243: for (Iterator it = transitionElements.iterator(); it.hasNext();) {
1244: Element transitionElement = (Element) it.next();
1245: if (StringUtils.hasText(transitionElement
1246: .getAttribute(ON_EXCEPTION_ATTRIBUTE))) {
1247: // the "on-exception transitions" are not really transitions but rather
1248: // FlowExecutionExceptionHandlers
1249: exceptionHandlers
1250: .add(parseTransitionExecutingExceptionHandler(transitionElement));
1251: }
1252: }
1253: return (FlowExecutionExceptionHandler[]) exceptionHandlers
1254: .toArray(new FlowExecutionExceptionHandler[exceptionHandlers
1255: .size()]);
1256: }
1257:
1258: private FlowExecutionExceptionHandler parseTransitionExecutingExceptionHandler(
1259: Element element) {
1260: TransitionExecutingStateExceptionHandler handler = new TransitionExecutingStateExceptionHandler();
1261: Class exceptionClass = (Class) fromStringTo(Class.class)
1262: .execute(element.getAttribute(ON_EXCEPTION_ATTRIBUTE));
1263: TargetStateResolver targetStateResolver = (TargetStateResolver) fromStringTo(
1264: TargetStateResolver.class).execute(
1265: element.getAttribute(TO_ATTRIBUTE));
1266: handler.add(exceptionClass, targetStateResolver);
1267: handler.getActionList().addAll(parseAnnotatedActions(element));
1268: return handler;
1269: }
1270:
1271: private FlowExecutionExceptionHandler[] parseCustomExceptionHandlers(
1272: Element element) {
1273: List exceptionHandlers = new LinkedList();
1274: List handlerElements = DomUtils.getChildElementsByTagName(
1275: element, EXCEPTION_HANDLER_ELEMENT);
1276: for (int i = 0; i < handlerElements.size(); i++) {
1277: Element handlerElement = (Element) handlerElements.get(i);
1278: exceptionHandlers
1279: .add(parseCustomExceptionHandler(handlerElement));
1280: }
1281: return (FlowExecutionExceptionHandler[]) exceptionHandlers
1282: .toArray(new FlowExecutionExceptionHandler[exceptionHandlers
1283: .size()]);
1284: }
1285:
1286: private FlowExecutionExceptionHandler parseCustomExceptionHandler(
1287: Element element) {
1288: return getLocalFlowServiceLocator().getExceptionHandler(
1289: element.getAttribute(BEAN_ATTRIBUTE));
1290: }
1291: }
|