View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.argeo.slc.core.execution.xml;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.argeo.slc.SlcException;
24  import org.argeo.slc.core.execution.DefaultExecutionFlow;
25  import org.argeo.slc.execution.ExecutionFlow;
26  import org.springframework.beans.factory.BeanDefinitionStoreException;
27  import org.springframework.beans.factory.config.RuntimeBeanReference;
28  import org.springframework.beans.factory.support.AbstractBeanDefinition;
29  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
30  import org.springframework.beans.factory.support.ManagedList;
31  import org.springframework.beans.factory.support.ManagedMap;
32  import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
33  import org.springframework.beans.factory.xml.ParserContext;
34  import org.springframework.util.StringUtils;
35  import org.springframework.util.xml.DomUtils;
36  import org.w3c.dom.Element;
37  import org.w3c.dom.Node;
38  import org.w3c.dom.NodeList;
39  
40  /** Interprets the <flow:flow> tag */
41  public class FlowBeanDefinitionParser extends
42  		AbstractSingleBeanDefinitionParser {
43  	private Log log = LogFactory.getLog(FlowBeanDefinitionParser.class);
44  
45  	/** Whether the user has already be warned on path attribute usage. */
46  	private Boolean warnedAboutPathAttribute = false;
47  
48  	@Override
49  	protected void doParse(Element element, ParserContext parserContext,
50  			BeanDefinitionBuilder builder) {
51  		String path = element.getAttribute("path");
52  		if (StringUtils.hasText(path)) {
53  			builder.addPropertyValue("path", path);
54  
55  			// warns user only once
56  			if (!warnedAboutPathAttribute)
57  				log.warn("The path=\"\" attribute is deprecated"
58  						+ " and will be removed in a later release."
59  						+ " Use <flow:flow name=\"/my/path/flowName\">.");
60  			warnedAboutPathAttribute = true;
61  		}
62  
63  		String spec = element.getAttribute("spec");
64  		if (StringUtils.hasText(spec))
65  			builder.getBeanDefinition().getConstructorArgumentValues()
66  					.addGenericArgumentValue(new RuntimeBeanReference(spec));
67  
68  		String abstrac = element.getAttribute("abstract");
69  		if (StringUtils.hasText(abstrac))
70  			builder.setAbstract(Boolean.parseBoolean(abstrac));
71  
72  		String parent = element.getAttribute("parent");
73  		if (StringUtils.hasText(parent))
74  			builder.setParentName(parent);
75  
76  		builder.getBeanDefinition().setDescription(
77  				DomUtils.getChildElementValueByTagName(element, "description"));
78  
79  		List<Element> argsElems = new ArrayList<Element>();
80  		List<Element> execElems = new ArrayList<Element>();
81  		List<Element> specElems = new ArrayList<Element>();
82  		NodeList nodeList = element.getChildNodes();
83  		for (int i = 0; i < nodeList.getLength(); i++) {
84  			Node node = nodeList.item(i);
85  			if (node instanceof Element) {
86  				if (DomUtils.nodeNameEquals(node, "arg"))
87  					argsElems.add((Element) node);
88  				else if (DomUtils.nodeNameEquals(node, "spec"))
89  					specElems.add((Element) node);
90  				else if (!DomUtils.nodeNameEquals(node, "description"))
91  					execElems.add((Element) node);
92  			}
93  		}
94  
95  		// Arguments
96  		if (argsElems.size() != 0) {
97  			ManagedMap<String, Object> args = new ManagedMap<String, Object>(
98  					argsElems.size());
99  			for (Element argElem : argsElems) {
100 				Object value = NamespaceUtils.parseValue(argElem,
101 						parserContext, builder.getBeanDefinition(), null);
102 				if (value != null)
103 					args.put(argElem.getAttribute("name"), value);
104 				else
105 					throw new SlcException("No value defined.");
106 			}
107 			builder.getBeanDefinition().getConstructorArgumentValues()
108 					.addGenericArgumentValue(args);
109 		}
110 
111 		// Execution specs
112 		if (StringUtils.hasText(spec) && specElems.size() != 0)
113 			throw new SlcException("A flow cannot have multiple specs");
114 		if (specElems.size() == 1) {
115 			Object specObj = NamespaceUtils.parseBeanOrReference(
116 					specElems.get(0), parserContext,
117 					builder.getBeanDefinition());
118 			builder.getBeanDefinition().getConstructorArgumentValues()
119 					.addGenericArgumentValue(specObj);
120 		} else if (specElems.size() > 1)
121 			throw new SlcException("A flow cannot have multiple specs");
122 
123 		// Executables
124 		if (execElems.size() != 0) {
125 			ManagedList<Object> executables = new ManagedList<Object>(
126 					execElems.size());
127 			for (Element child : execElems) {
128 				// child validity check is performed in xsd
129 				executables.add(NamespaceUtils.parseBeanOrReference(child,
130 						parserContext, builder.getBeanDefinition()));
131 			}
132 			if (executables.size() > 0)
133 				builder.addPropertyValue("executables", executables);
134 		}
135 	}
136 
137 	@SuppressWarnings("unchecked")
138 	@Override
139 	protected Class<? extends ExecutionFlow> getBeanClass(Element element) {
140 		String clss = element.getAttribute("class");
141 		if (StringUtils.hasText(clss))
142 			// TODO: check that it actually works
143 			try {
144 				return (Class<? extends ExecutionFlow>) getClass()
145 						.getClassLoader().loadClass(clss);
146 			} catch (ClassNotFoundException e) {
147 				try {
148 					return (Class<? extends ExecutionFlow>) Thread
149 							.currentThread().getContextClassLoader()
150 							.loadClass(clss);
151 				} catch (ClassNotFoundException e1) {
152 					throw new SlcException("Cannot load class " + clss, e);
153 				}
154 			}
155 		else
156 			return DefaultExecutionFlow.class;
157 	}
158 
159 	// parse nested bean definition
160 	// private Object parseBeanReference(Element element,
161 	// ParserContext parserContext, BeanDefinitionBuilder builder) {
162 	// return parserContext.getDelegate().parsePropertySubElement(element,
163 	// builder.getBeanDefinition());
164 	// }
165 
166 	@Override
167 	protected String resolveId(Element element,
168 			AbstractBeanDefinition definition, ParserContext parserContext)
169 			throws BeanDefinitionStoreException {
170 		String name = element.getAttribute("name");
171 		if (StringUtils.hasText(name)) {
172 			return name;
173 		} else {
174 			return super.resolveId(element, definition, parserContext);
175 		}
176 	}
177 
178 	protected boolean shouldGenerateIdAsFallback() {
179 		return true;
180 	}
181 
182 }