001: /*
002: * Copyright (c) 2002-2003 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.workflow.loader;
006:
007: import com.opensymphony.workflow.FactoryException;
008: import com.opensymphony.workflow.InvalidWorkflowDescriptorException;
009:
010: import org.w3c.dom.*;
011:
012: import java.io.*;
013:
014: import java.net.URL;
015:
016: import java.util.*;
017:
018: import javax.xml.parsers.*;
019:
020: /**
021: * @author Hani Suleiman
022: * Date: May 10, 2002
023: * Time: 11:30:41 AM
024: */
025: public class XMLWorkflowFactory extends AbstractWorkflowFactory
026: implements Serializable {
027: //~ Static fields/initializers /////////////////////////////////////////////
028:
029: private static final long serialVersionUID = 452755218478437087L;
030:
031: //~ Instance fields ////////////////////////////////////////////////////////
032:
033: protected Map workflows;
034: protected boolean reload;
035:
036: //~ Methods ////////////////////////////////////////////////////////////////
037:
038: public void setLayout(String workflowName, Object layout) {
039: }
040:
041: public Object getLayout(String workflowName) {
042: return null;
043: }
044:
045: public boolean isModifiable(String name) {
046: return true;
047: }
048:
049: public String getName() {
050: return "";
051: }
052:
053: public WorkflowDescriptor getWorkflow(String name, boolean validate)
054: throws FactoryException {
055: WorkflowConfig c = (WorkflowConfig) workflows.get(name);
056:
057: if (c == null) {
058: throw new FactoryException("Unknown workflow name \""
059: + name + '\"');
060: }
061:
062: if (c.descriptor != null) {
063: if (reload) {
064: File file = new File(c.url.getFile());
065:
066: if (file.exists()
067: && (file.lastModified() > c.lastModified)) {
068: c.lastModified = file.lastModified();
069: loadWorkflow(c, validate);
070: }
071: }
072: } else {
073: loadWorkflow(c, validate);
074: }
075:
076: c.descriptor.setName(name);
077:
078: return c.descriptor;
079: }
080:
081: public String[] getWorkflowNames() {
082: int i = 0;
083: String[] res = new String[workflows.keySet().size()];
084: Iterator it = workflows.keySet().iterator();
085:
086: while (it.hasNext()) {
087: res[i++] = (String) it.next();
088: }
089:
090: return res;
091: }
092:
093: public void createWorkflow(String name) {
094: }
095:
096: public void initDone() throws FactoryException {
097: reload = getProperties().getProperty("reload", "false").equals(
098: "true");
099:
100: String name = getProperties().getProperty("resource",
101: "workflows.xml");
102: InputStream is = getInputStream(name);
103:
104: if (is == null) {
105: throw new FactoryException(
106: "Unable to find workflows file '" + name
107: + "' in classpath");
108: }
109:
110: try {
111: DocumentBuilderFactory dbf = DocumentBuilderFactory
112: .newInstance();
113: dbf.setNamespaceAware(true);
114:
115: DocumentBuilder db;
116:
117: try {
118: db = dbf.newDocumentBuilder();
119: } catch (ParserConfigurationException e) {
120: throw new FactoryException(
121: "Error creating document builder", e);
122: }
123:
124: Document doc = db.parse(is);
125:
126: Element root = (Element) doc.getElementsByTagName(
127: "workflows").item(0);
128: workflows = new HashMap();
129:
130: String basedir = getBaseDir(root);
131:
132: List list = XMLUtil.getChildElements(root, "workflow");
133:
134: for (int i = 0; i < list.size(); i++) {
135: Element e = (Element) list.get(i);
136: WorkflowConfig config = new WorkflowConfig(basedir, e
137: .getAttribute("type"), e
138: .getAttribute("location"));
139: workflows.put(e.getAttribute("name"), config);
140: }
141: } catch (Exception e) {
142: throw new InvalidWorkflowDescriptorException(
143: "Error in workflow config", e);
144: }
145: }
146:
147: public boolean removeWorkflow(String name) throws FactoryException {
148: throw new FactoryException("remove workflow not supported");
149:
150: //WorkflowConfig workflow = (WorkflowConfig)workflows.remove(name);
151: //if(workflow == null) return false;
152: //if(workflow.descriptor != null)
153: //{
154: //
155: //}
156: //return true;
157: }
158:
159: public void renameWorkflow(String oldName, String newName) {
160: }
161:
162: public void save() {
163: }
164:
165: public boolean saveWorkflow(String name,
166: WorkflowDescriptor descriptor, boolean replace)
167: throws FactoryException {
168: WorkflowConfig c = (WorkflowConfig) workflows.get(name);
169:
170: if ((c != null) && !replace) {
171: return false;
172: }
173:
174: if (c == null) {
175: throw new UnsupportedOperationException(
176: "Saving of new workflow is not currently supported");
177: }
178:
179: Writer out;
180:
181: // [KAP] comment this line to disable all the validation while saving a workflow
182: //descriptor.validate();
183: try {
184: out = new OutputStreamWriter(new FileOutputStream(c.url
185: .getFile()
186: + ".new"), "utf-8");
187: } catch (FileNotFoundException ex) {
188: throw new FactoryException(
189: "Could not create new file to save workflow "
190: + c.url.getFile());
191: } catch (UnsupportedEncodingException ex) {
192: throw new FactoryException(
193: "utf-8 encoding not supported, contact your JVM vendor!");
194: }
195:
196: writeXML(descriptor, out);
197:
198: //write it out to a new file, to ensure we don't end up with a messed up file if we're interrupted halfway for some reason
199: //now lets rename
200: File original = new File(c.url.getFile());
201: File backup = new File(c.url.getFile() + ".bak");
202: File updated = new File(c.url.getFile() + ".new");
203: boolean isOK = !original.exists() || original.renameTo(backup);
204:
205: if (!isOK) {
206: throw new FactoryException(
207: "Unable to backup original workflow file "
208: + original + " to " + backup
209: + ", aborting save");
210: }
211:
212: isOK = updated.renameTo(original);
213:
214: if (!isOK) {
215: throw new FactoryException(
216: "Unable to rename new workflow file " + updated
217: + " to " + original + ", aborting save");
218: }
219:
220: backup.delete();
221:
222: return true;
223: }
224:
225: /**
226: * Get where to find workflow XML files.
227: * @param root The root element of the XML file.
228: * @return The absolute base dir used for finding these files or null.
229: */
230: protected String getBaseDir(Element root) {
231: String basedir = root.getAttribute("basedir");
232:
233: if (basedir.length() == 0) {
234: // No base dir defined
235: return null;
236: }
237:
238: if (new File(basedir).isAbsolute()) {
239: // An absolute base dir defined
240: return basedir;
241: } else {
242: // Append the current working directory to the relative base dir
243: return new File(System.getProperty("user.dir"), basedir)
244: .getAbsolutePath();
245: }
246: }
247:
248: /**
249: * Load the workflow config file from the current context classloader.
250: * The search order is:
251: * <li>Specified URL</li>
252: * <li><name></li>
253: * <li>/<name></li>
254: * <li>META-INF/<name></li>
255: * <li>/META-INF/<name></li>
256: */
257: protected InputStream getInputStream(String name) {
258: ClassLoader classLoader = Thread.currentThread()
259: .getContextClassLoader();
260: InputStream is = null;
261:
262: if ((name != null) && (name.indexOf(":/") > -1)) {
263: try {
264: is = new URL(name).openStream();
265: } catch (Exception e) {
266: }
267: }
268:
269: if (is == null) {
270: try {
271: is = classLoader.getResourceAsStream(name);
272: } catch (Exception e) {
273: }
274: }
275:
276: if (is == null) {
277: try {
278: is = classLoader.getResourceAsStream('/' + name);
279: } catch (Exception e) {
280: }
281: }
282:
283: if (is == null) {
284: try {
285: is = classLoader
286: .getResourceAsStream("META-INF/" + name);
287: } catch (Exception e) {
288: }
289: }
290:
291: if (is == null) {
292: try {
293: is = classLoader.getResourceAsStream("/META-INF/"
294: + name);
295: } catch (Exception e) {
296: }
297: }
298:
299: return is;
300: }
301:
302: protected void writeXML(WorkflowDescriptor descriptor, Writer out) {
303: PrintWriter writer = new PrintWriter(new BufferedWriter(out));
304: writer.println(WorkflowDescriptor.XML_HEADER);
305: writer.println(WorkflowDescriptor.DOCTYPE_DECL);
306: descriptor.writeXML(writer, 0);
307: writer.flush();
308: writer.close();
309: }
310:
311: private void loadWorkflow(WorkflowConfig c, boolean validate)
312: throws FactoryException {
313: try {
314: c.descriptor = WorkflowLoader.load(c.url, validate);
315: } catch (Exception e) {
316: throw new FactoryException("Error in workflow descriptor: "
317: + c.url, e);
318: }
319: }
320:
321: //~ Inner Classes //////////////////////////////////////////////////////////
322:
323: static class WorkflowConfig implements Serializable {
324: private static final long serialVersionUID = 4939957922893602958L;
325: String location;
326: String type; // file, URL, service
327: URL url;
328: WorkflowDescriptor descriptor;
329: long lastModified;
330:
331: public WorkflowConfig(String basedir, String type,
332: String location) {
333: if ("URL".equals(type)) {
334: try {
335: url = new URL(location);
336:
337: File file = new File(url.getFile());
338:
339: if (file.exists()) {
340: lastModified = file.lastModified();
341: }
342: } catch (Exception ex) {
343: }
344: } else if ("file".equals(type)) {
345: try {
346: File file = new File(basedir, location);
347: url = file.toURL();
348: lastModified = file.lastModified();
349: } catch (Exception ex) {
350: }
351: } else {
352: url = Thread.currentThread().getContextClassLoader()
353: .getResource(location);
354: }
355:
356: this.type = type;
357: this.location = location;
358: }
359: }
360: }
|