1 17 18 19 20 package org.apache.lenya.workflow.impl; 21 22 import java.io.File ; 23 import java.io.FileInputStream ; 24 import java.io.FileOutputStream ; 25 import java.io.IOException ; 26 import java.nio.channels.FileChannel ; 27 import java.text.SimpleDateFormat ; 28 import java.util.ArrayList ; 29 import java.util.Date ; 30 import java.util.List ; 31 32 import javax.xml.transform.TransformerException ; 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 ; 46 import org.w3c.dom.Element ; 47 48 58 public abstract class History implements WorkflowListener { 59 public static final String WORKFLOW_ATTRIBUTE = "workflow"; 60 public static final String HISTORY_ELEMENT = "history"; 61 public static final String VERSION_ELEMENT = "version"; 62 public static final String STATE_ATTRIBUTE = "state"; 63 public static final String EVENT_ATTRIBUTE = "event"; 64 public static final String VARIABLE_ELEMENT = "variable"; 65 public static final String NAME_ATTRIBUTE = "name"; 66 public static final String VALUE_ATTRIBUTE = "value"; 67 public static final String DATE_ATTRIBUTE = "date"; 68 69 75 public void initialize(String workflowId, Situation situation) throws WorkflowException { 76 try { 77 File 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 historyElement = helper.getDocument().getDocumentElement(); 85 historyElement.setAttribute(WORKFLOW_ATTRIBUTE, workflowId); 86 87 Element initialVersionElement = createInitialVersionElement(helper, situation, 88 workflowId); 89 historyElement.appendChild(initialVersionElement); 90 91 DocumentHelper.writeDocument(helper.getDocument(), file); 92 } catch (Exception e) { 93 throw new WorkflowException(e); 94 } 95 } 96 97 100 protected History() { 101 } 102 103 private WorkflowInstanceImpl instance = null; 104 105 110 protected NamespaceHelper getNamespaceHelper() throws WorkflowException { 111 NamespaceHelper helper; 112 try { 113 Document document = DocumentHelper.readDocument(getHistoryFile()); 114 helper = new NamespaceHelper(Workflow.NAMESPACE, Workflow.DEFAULT_PREFIX, document); 115 } catch (Exception e) { 116 throw new WorkflowException(e); 117 } 118 return helper; 119 } 120 121 126 protected String getWorkflowId() throws WorkflowException { 127 return getWorkflowId(getNamespaceHelper()); 128 } 129 130 136 protected String getWorkflowId(NamespaceHelper helper) throws WorkflowException { 137 String 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 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 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 185 public boolean isInitialized() { 186 return getHistoryFile().exists(); 187 } 188 189 193 protected abstract File getHistoryFile(); 194 195 200 protected abstract WorkflowInstanceImpl createInstance() throws WorkflowException; 201 202 210 protected Element createInitialVersionElement(NamespaceHelper helper, Situation situation, 211 String workflowId) throws WorkflowException { 212 Element 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 231 protected Element createVersionElement(NamespaceHelper helper, StateImpl state, 232 Situation situation, Event event) { 233 Element versionElement = createVersionElement(helper, situation); 234 versionElement.setAttribute(STATE_ATTRIBUTE, state.getId()); 235 versionElement.setAttribute(EVENT_ATTRIBUTE, event.getName()); 236 return versionElement; 237 } 238 239 245 protected Element createVersionElement(NamespaceHelper helper, Situation situation) { 246 Element versionElement = helper.createElement(VERSION_ELEMENT); 247 Date now = new Date (); 248 SimpleDateFormat format = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss"); 249 String dateString = format.format(now); 250 versionElement.setAttribute(DATE_ATTRIBUTE, dateString); 251 return versionElement; 252 } 253 254 263 public void transitionFired(WorkflowInstance instance, Situation situation, Event event) 264 throws WorkflowException { 265 try { 266 org.w3c.dom.Document xmlDocument = DocumentHelper.readDocument(getHistoryFile()); 267 Element root = xmlDocument.getDocumentElement(); 268 269 NamespaceHelper helper = new NamespaceHelper(Workflow.NAMESPACE, 270 Workflow.DEFAULT_PREFIX, xmlDocument); 271 272 Element 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 e) { 281 throw new WorkflowException(e); 282 } 283 } 284 285 289 public void setInstance(WorkflowInstanceImpl impl) { 290 instance = impl; 291 } 292 293 298 protected void saveVariables(NamespaceHelper helper) throws WorkflowException { 299 Element parent = helper.getDocument().getDocumentElement(); 300 BooleanVariable[] variables = getInstance().getWorkflowImpl().getVariables(); 301 302 for (int i = 0; i < variables.length; i++) { 303 String name = variables[i].getName(); 304 boolean value = getInstance().getValue(name); 305 306 try { 307 Element element = (Element ) 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 e) { 318 throw new WorkflowException(e); 319 } 320 } 321 } 322 323 329 protected void restoreVariables(WorkflowInstanceImpl instance, NamespaceHelper helper) 330 throws WorkflowException { 331 Element parent = helper.getDocument().getDocumentElement(); 332 333 Element [] variableElements = helper.getChildren(parent, VARIABLE_ELEMENT); 334 335 for (int i = 0; i < variableElements.length; i++) { 336 String name = variableElements[i].getAttribute(NAME_ATTRIBUTE); 337 String value = variableElements[i].getAttribute(VALUE_ATTRIBUTE); 338 instance.setValue(name, Boolean.valueOf(value).booleanValue()); 339 } 340 } 341 342 348 protected void restoreState(WorkflowInstanceImpl instance, NamespaceHelper helper) 349 throws WorkflowException { 350 State state; 351 Element [] versionElements = helper.getChildren(helper.getDocument().getDocumentElement(), 352 VERSION_ELEMENT); 353 354 if (versionElements.length > 0) { 355 Element lastElement = versionElements[versionElements.length - 1]; 356 String 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 370 protected void move(File newFile) throws WorkflowException { 371 372 FileInputStream sourceStream = null; 373 FileOutputStream destinationStream = null; 374 FileChannel sourceChannel = null; 375 FileChannel destinationChannel = null; 376 try { 377 newFile.getParentFile().mkdirs(); 378 newFile.createNewFile(); 379 File historyFile = getHistoryFile(); 380 381 sourceStream = new FileInputStream (historyFile); 382 sourceChannel = sourceStream.getChannel(); 383 384 destinationStream = new FileOutputStream (newFile); 385 destinationChannel = destinationStream.getChannel(); 386 387 destinationChannel.transferFrom(sourceChannel, 0, sourceChannel.size()); 388 sourceStream.close(); 389 destinationStream.close(); 390 File 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 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 e) { 407 throw new WorkflowException(e); 408 } 409 } 410 } 411 412 419 protected Version restoreVersion(NamespaceHelper helper, Element 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 eventId = element.getAttribute(EVENT_ATTRIBUTE); 427 if (eventId != null && !"".equals(eventId)) { 428 event = getInstance().getWorkflowImpl().getEvent(eventId); 429 } 430 431 String 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 443 public Version[] getVersions() throws WorkflowException { 444 List versions = new ArrayList (); 445 446 NamespaceHelper helper = getNamespaceHelper(); 447 Element documentElement = helper.getDocument().getDocumentElement(); 448 Element [] 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 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 |