KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > lenya > workflow > impl > History


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
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  */

17
18 /* $Id: History.java 126321 2005-01-24 22:16:29Z gregor $ */
19
20 package org.apache.lenya.workflow.impl;
21
22 import java.io.File JavaDoc;
23 import java.io.FileInputStream JavaDoc;
24 import java.io.FileOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.nio.channels.FileChannel JavaDoc;
27 import java.text.SimpleDateFormat JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.Date JavaDoc;
30 import java.util.List JavaDoc;
31
32 import javax.xml.transform.TransformerException JavaDoc;
33
34 import org.apache.lenya.workflow.BooleanVariable;
35 import org.apache.lenya.workflow.Event;
36 import org.apache.lenya.workflow.Situation;
37 import org.apache.lenya.workflow.State;
38 import org.apache.lenya.workflow.Workflow;
39 import org.apache.lenya.workflow.WorkflowException;
40 import org.apache.lenya.workflow.WorkflowInstance;
41 import org.apache.lenya.workflow.WorkflowListener;
42 import org.apache.lenya.xml.DocumentHelper;
43 import org.apache.lenya.xml.NamespaceHelper;
44 import org.apache.xpath.XPathAPI;
45 import org.w3c.dom.Document JavaDoc;
46 import org.w3c.dom.Element JavaDoc;
47
48 /**
49  * <p>
50  * The history of a workflow instance contains a list of all versions of the instance. A version
51  * contains
52  * </p>
53  * <ul>
54  * <li>the state,</li>
55  * <li>the event that caused the transition (omitted in the first version).</li>
56  * </ul>
57  */

58 public abstract class History implements WorkflowListener {
59     public static final String JavaDoc WORKFLOW_ATTRIBUTE = "workflow";
60     public static final String JavaDoc HISTORY_ELEMENT = "history";
61     public static final String JavaDoc VERSION_ELEMENT = "version";
62     public static final String JavaDoc STATE_ATTRIBUTE = "state";
63     public static final String JavaDoc EVENT_ATTRIBUTE = "event";
64     public static final String JavaDoc VARIABLE_ELEMENT = "variable";
65     public static final String JavaDoc NAME_ATTRIBUTE = "name";
66     public static final String JavaDoc VALUE_ATTRIBUTE = "value";
67     public static final String JavaDoc DATE_ATTRIBUTE = "date";
68
69     /**
70      * Creates a new history object. A new history file is created and initialized.
71      * @param workflowId The workflow ID.
72      * @param situation The current situation.
73      * @throws WorkflowException when something went wrong.
74      */

75     public void initialize(String JavaDoc workflowId, Situation situation) throws WorkflowException {
76         try {
77             File JavaDoc file = getHistoryFile();
78             file.getParentFile().mkdirs();
79             file.createNewFile();
80
81             NamespaceHelper helper = new NamespaceHelper(Workflow.NAMESPACE,
82                     Workflow.DEFAULT_PREFIX, HISTORY_ELEMENT);
83
84             Element JavaDoc historyElement = helper.getDocument().getDocumentElement();
85             historyElement.setAttribute(WORKFLOW_ATTRIBUTE, workflowId);
86
87             Element JavaDoc initialVersionElement = createInitialVersionElement(helper, situation,
88                     workflowId);
89             historyElement.appendChild(initialVersionElement);
90
91             DocumentHelper.writeDocument(helper.getDocument(), file);
92         } catch (Exception JavaDoc e) {
93             throw new WorkflowException(e);
94         }
95     }
96
97     /**
98      * Creates a new history object for a workflow instance.
99      */

100     protected History() {
101     }
102
103     private WorkflowInstanceImpl instance = null;
104
105     /**
106      * Returns the namespace helper for the history file.
107      * @return A namespace helper.
108      * @throws WorkflowException It the helper could not be obtained.
109      */

110     protected NamespaceHelper getNamespaceHelper() throws WorkflowException {
111         NamespaceHelper helper;
112         try {
113             Document JavaDoc document = DocumentHelper.readDocument(getHistoryFile());
114             helper = new NamespaceHelper(Workflow.NAMESPACE, Workflow.DEFAULT_PREFIX, document);
115         } catch (Exception JavaDoc e) {
116             throw new WorkflowException(e);
117         }
118         return helper;
119     }
120
121     /**
122      * Returns the workflow ID for this history.
123      * @return A string.
124      * @throws WorkflowException when something went wrong.
125      */

126     protected String JavaDoc getWorkflowId() throws WorkflowException {
127         return getWorkflowId(getNamespaceHelper());
128     }
129
130     /**
131      * Returns the workflow ID for this history.
132      * @param helper The namespace helper for the history document.
133      * @return A string.
134      * @throws WorkflowException when something went wrong.
135      */

136     protected String JavaDoc getWorkflowId(NamespaceHelper helper) throws WorkflowException {
137         String JavaDoc workflowId = helper.getDocument().getDocumentElement().getAttribute(
138                 WORKFLOW_ATTRIBUTE);
139         if (workflowId == null) {
140             throw new WorkflowException("The attribute '" + WORKFLOW_ATTRIBUTE + "' is missing!");
141         }
142         if ("".equals(workflowId)) {
143             throw new WorkflowException("The workflow ID must not be empty!");
144         }
145         return workflowId;
146     }
147
148     /**
149      * Restores the workflow, state and variables of a workflow instance from this history.
150      * @return The workflow instance to restore.
151      * @throws WorkflowException if something goes wrong.
152      */

153     public WorkflowInstanceImpl getInstance() throws WorkflowException {
154         if (this.instance == null) {
155             if (!isInitialized()) {
156                 throw new WorkflowException(
157                         "The workflow history has not been initialized: The file ["
158                                 + getHistoryFile() + "] is missing.");
159             }
160
161             WorkflowInstanceImpl instance = createInstance();
162             NamespaceHelper helper = getNamespaceHelper();
163
164             String JavaDoc workflowId = getWorkflowId(helper);
165             if (null == workflowId) {
166                 throw new WorkflowException("No workflow attribute set in history document!");
167             }
168
169             instance.setWorkflow(workflowId);
170
171             restoreState(instance, helper);
172             restoreVariables(instance, helper);
173
174             instance.addWorkflowListener(this);
175             setInstance(instance);
176         }
177
178         return instance;
179     }
180
181     /**
182      * Returns if the history has been initialized.
183      * @return A boolean value.
184      */

185     public boolean isInitialized() {
186         return getHistoryFile().exists();
187     }
188
189     /**
190      * Factory method to obtain the history file.
191      * @return A file.
192      */

193     protected abstract File JavaDoc getHistoryFile();
194
195     /**
196      * Factory method to create a workflow instance object.
197      * @return A workflow instance object.
198      * @throws WorkflowException if something goes wrong.
199      */

200     protected abstract WorkflowInstanceImpl createInstance() throws WorkflowException;
201
202     /**
203      * Creates a new version element. This method is called after a tansition invocation.
204      * @param helper The namespace helper of the history document.
205      * @param situation The current situation.
206      * @param workflowId The workflow ID.
207      * @return An XML element.
208      * @throws WorkflowException when something went wrong.
209      */

210     protected Element JavaDoc createInitialVersionElement(NamespaceHelper helper, Situation situation,
211             String JavaDoc workflowId) throws WorkflowException {
212         Element JavaDoc versionElement = createVersionElement(helper, situation);
213
214         WorkflowInstanceImpl instance = createInstance();
215         instance.setWorkflow(workflowId);
216
217         StateImpl initialState = (StateImpl) instance.getWorkflow().getInitialState();
218         versionElement.setAttribute(STATE_ATTRIBUTE, initialState.getId());
219
220         return versionElement;
221     }
222
223     /**
224      * Creates a new version element. This method is called after a tansition invocation.
225      * @param helper The namespace helper of the history document.
226      * @param state The state of the new version.
227      * @param situation The current situation.
228      * @param event The event that was invoked.
229      * @return An XML element.
230      */

231     protected Element JavaDoc createVersionElement(NamespaceHelper helper, StateImpl state,
232             Situation situation, Event event) {
233         Element JavaDoc versionElement = createVersionElement(helper, situation);
234         versionElement.setAttribute(STATE_ATTRIBUTE, state.getId());
235         versionElement.setAttribute(EVENT_ATTRIBUTE, event.getName());
236         return versionElement;
237     }
238
239     /**
240      * Creates a version element based on a situation.
241      * @param helper The namespace helper of the history document.
242      * @param situation The current situation.
243      * @return An XML element.
244      */

245     protected Element JavaDoc createVersionElement(NamespaceHelper helper, Situation situation) {
246         Element JavaDoc versionElement = helper.createElement(VERSION_ELEMENT);
247         Date JavaDoc now = new Date JavaDoc();
248         SimpleDateFormat JavaDoc format = new SimpleDateFormat JavaDoc("yyyy-MM-dd HH:mm:ss");
249         String JavaDoc dateString = format.format(now);
250         versionElement.setAttribute(DATE_ATTRIBUTE, dateString);
251         return versionElement;
252     }
253
254     /**
255      * Advances the workflow history when a tranistion was invoked.
256      *
257      * @param instance The workflow instance.
258      * @param situation The situation before the transition.
259      * @param event The invoked event.
260      *
261      * @throws WorkflowException when something went wrong.
262      */

263     public void transitionFired(WorkflowInstance instance, Situation situation, Event event)
264             throws WorkflowException {
265         try {
266             org.w3c.dom.Document JavaDoc xmlDocument = DocumentHelper.readDocument(getHistoryFile());
267             Element JavaDoc root = xmlDocument.getDocumentElement();
268
269             NamespaceHelper helper = new NamespaceHelper(Workflow.NAMESPACE,
270                     Workflow.DEFAULT_PREFIX, xmlDocument);
271
272             Element JavaDoc versionElement = createVersionElement(helper, (StateImpl) instance
273                     .getCurrentState(), situation, event);
274
275             root.appendChild(versionElement);
276
277             saveVariables(helper);
278
279             DocumentHelper.writeDocument(xmlDocument, getHistoryFile());
280         } catch (Exception JavaDoc e) {
281             throw new WorkflowException(e);
282         }
283     }
284
285     /**
286      * Sets the workflow instance.
287      * @param impl A workflow instance implementation.
288      */

289     public void setInstance(WorkflowInstanceImpl impl) {
290         instance = impl;
291     }
292
293     /**
294      * Saves the state variables as children of the document element.
295      * @param helper The helper that holds the document.
296      * @throws WorkflowException when something went wrong.
297      */

298     protected void saveVariables(NamespaceHelper helper) throws WorkflowException {
299         Element JavaDoc parent = helper.getDocument().getDocumentElement();
300         BooleanVariable[] variables = getInstance().getWorkflowImpl().getVariables();
301
302         for (int i = 0; i < variables.length; i++) {
303             String JavaDoc name = variables[i].getName();
304             boolean value = getInstance().getValue(name);
305
306             try {
307                 Element JavaDoc element = (Element JavaDoc) XPathAPI.selectSingleNode(parent, "*[local-name() = '"
308                         + VARIABLE_ELEMENT + "']" + "[@" + NAME_ATTRIBUTE + " = '" + name + "']");
309
310                 if (element == null) {
311                     element = helper.createElement(VARIABLE_ELEMENT);
312                     element.setAttribute(NAME_ATTRIBUTE, name);
313                     parent.appendChild(element);
314                 }
315
316                 element.setAttribute(VALUE_ATTRIBUTE, Boolean.toString(value));
317             } catch (TransformerException JavaDoc e) {
318                 throw new WorkflowException(e);
319             }
320         }
321     }
322
323     /**
324      * Restores the state variables of a workflow instance.
325      * @param instance The instance to restore.
326      * @param helper The helper that wraps the history document.
327      * @throws WorkflowException when something went wrong.
328      */

329     protected void restoreVariables(WorkflowInstanceImpl instance, NamespaceHelper helper)
330             throws WorkflowException {
331         Element JavaDoc parent = helper.getDocument().getDocumentElement();
332
333         Element JavaDoc[] variableElements = helper.getChildren(parent, VARIABLE_ELEMENT);
334
335         for (int i = 0; i < variableElements.length; i++) {
336             String JavaDoc name = variableElements[i].getAttribute(NAME_ATTRIBUTE);
337             String JavaDoc value = variableElements[i].getAttribute(VALUE_ATTRIBUTE);
338             instance.setValue(name, Boolean.valueOf(value).booleanValue());
339         }
340     }
341
342     /**
343      * Restores the state of a workflow instance.
344      * @param instance The instance to restore.
345      * @param helper The helper that wraps the history document.
346      * @throws WorkflowException when something went wrong.
347      */

348     protected void restoreState(WorkflowInstanceImpl instance, NamespaceHelper helper)
349             throws WorkflowException {
350         State state;
351         Element JavaDoc[] versionElements = helper.getChildren(helper.getDocument().getDocumentElement(),
352                 VERSION_ELEMENT);
353
354         if (versionElements.length > 0) {
355             Element JavaDoc lastElement = versionElements[versionElements.length - 1];
356             String JavaDoc stateId = lastElement.getAttribute(STATE_ATTRIBUTE);
357             state = instance.getWorkflowImpl().getState(stateId);
358         } else {
359             state = instance.getWorkflow().getInitialState();
360         }
361
362         instance.setCurrentState(state);
363     }
364
365     /**
366      * Moves this history to a new file.
367      * @param newFile The new file.
368      * @throws WorkflowException when something went wrong.
369      */

370     protected void move(File JavaDoc newFile) throws WorkflowException {
371
372         FileInputStream JavaDoc sourceStream = null;
373         FileOutputStream JavaDoc destinationStream = null;
374         FileChannel JavaDoc sourceChannel = null;
375         FileChannel JavaDoc destinationChannel = null;
376         try {
377             newFile.getParentFile().mkdirs();
378             newFile.createNewFile();
379             File JavaDoc historyFile = getHistoryFile();
380
381             sourceStream = new FileInputStream JavaDoc(historyFile);
382             sourceChannel = sourceStream.getChannel();
383
384             destinationStream = new FileOutputStream JavaDoc(newFile);
385             destinationChannel = destinationStream.getChannel();
386
387             destinationChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
388             sourceStream.close();
389             destinationStream.close();
390             File JavaDoc directory = historyFile.getParentFile();
391             boolean deleted = historyFile.delete();
392             if (!deleted) {
393                 throw new WorkflowException("The old history file could not be deleted!");
394             }
395             if (directory.exists() && directory.isDirectory() && directory.listFiles().length == 0) {
396                 directory.delete();
397             }
398         } catch (IOException JavaDoc e) {
399             throw new WorkflowException(e);
400         } finally {
401             try {
402                 if (sourceStream != null)
403                     sourceStream.close();
404                 if (destinationStream != null)
405                     destinationStream.close();
406             } catch (IOException JavaDoc e) {
407                 throw new WorkflowException(e);
408             }
409         }
410     }
411
412     /**
413      * Restores a version from an XML element.
414      * @param helper The namespace helper.
415      * @param element An XML element.
416      * @return A version.
417      * @throws WorkflowException when something went wrong.
418      */

419     protected Version restoreVersion(NamespaceHelper helper, Element JavaDoc element)
420             throws WorkflowException {
421         if (!element.getLocalName().equals(VERSION_ELEMENT)) {
422             throw new WorkflowException("Invalid history XML!");
423         }
424
425         Event event = null;
426         String JavaDoc eventId = element.getAttribute(EVENT_ATTRIBUTE);
427         if (eventId != null && !"".equals(eventId)) {
428             event = getInstance().getWorkflowImpl().getEvent(eventId);
429         }
430
431         String JavaDoc stateId = element.getAttribute(STATE_ATTRIBUTE);
432         State state = getInstance().getWorkflowImpl().getState(stateId);
433
434         Version version = new Version(event, state);
435         return version;
436     }
437
438     /**
439      * Returns the versions of this history.
440      * @return An array of versions.
441      * @throws WorkflowException when something went wrong.
442      */

443     public Version[] getVersions() throws WorkflowException {
444         List JavaDoc versions = new ArrayList JavaDoc();
445
446         NamespaceHelper helper = getNamespaceHelper();
447         Element JavaDoc documentElement = helper.getDocument().getDocumentElement();
448         Element JavaDoc[] versionElements = getNamespaceHelper().getChildren(documentElement,
449                 VERSION_ELEMENT);
450
451         for (int i = 0; i < versionElements.length; i++) {
452             Version version = restoreVersion(helper, versionElements[i]);
453             versions.add(version);
454         }
455         return (Version[]) versions.toArray(new Version[versions.size()]);
456     }
457
458     /**
459      * Deletes the history.
460      * @throws WorkflowException when something went wrong.
461      */

462     public void delete() throws WorkflowException {
463         System.out.println("Deleting file [" + getHistoryFile() + "]");
464         if (!isInitialized()) {
465             throw new WorkflowException("The workflow history is not initialized!");
466         }
467
468         boolean deleted = getHistoryFile().delete();
469
470         if (!deleted) {
471             throw new WorkflowException("The workflow history could not be deleted!");
472         }
473
474     }
475
476 }
Popular Tags