001/* 002 * Copyright 2010 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.workflow.store; 017 018import java.util.ArrayList; 019import java.util.Date; 020import java.util.GregorianCalendar; 021import java.util.LinkedHashSet; 022import java.util.List; 023import java.util.Set; 024 025import javax.jcr.Node; 026import javax.jcr.Property; 027import javax.jcr.PropertyIterator; 028import javax.jcr.PropertyType; 029import javax.jcr.RepositoryException; 030import javax.jcr.Session; 031import javax.jcr.Value; 032import javax.jcr.ValueFactory; 033 034import org.ametys.plugins.repository.AmetysRepositoryException; 035 036import com.opensymphony.workflow.spi.Step; 037 038/** 039 * OSWorkflow step with additional properties.<br> 040 */ 041public class AmetysStep implements Step 042{ 043 /** Custom property prefix. */ 044 protected static final String __CUSTOM_PROP_PREFIX = "ametys:"; 045 046 private Node _node; 047 private AbstractJackrabbitWorkflowStore _store; 048 049 /** 050 * Build an ametys step. 051 * @param node the backing JCR node. 052 * @param store the workflow store. 053 */ 054 public AmetysStep(Node node, AbstractJackrabbitWorkflowStore store) 055 { 056 this._node = node; 057 this._store = store; 058 } 059 060 /** 061 * Get the backing JCR node. 062 * @return the backing JCR node. 063 */ 064 Node getNode() 065 { 066 return _node; 067 } 068 069 @Override 070 public long getId() 071 { 072 try 073 { 074 return _node.getProperty(AbstractJackrabbitWorkflowStore.__ID_PROPERTY).getLong(); 075 } 076 catch (RepositoryException e) 077 { 078 throw new AmetysRepositoryException("Error getting the ID property.", e); 079 } 080 } 081 082 @Override 083 public long getEntryId() 084 { 085 try 086 { 087 return _node.getParent().getProperty(AbstractJackrabbitWorkflowStore.__ID_PROPERTY).getLong(); 088 } 089 catch (RepositoryException e) 090 { 091 throw new AmetysRepositoryException("Error getting the entry ID property.", e); 092 } 093 } 094 095 @Override 096 public int getStepId() 097 { 098 try 099 { 100 return (int) _node.getProperty(AbstractJackrabbitWorkflowStore.__STEP_ID_PROPERTY).getLong(); 101 } 102 catch (RepositoryException e) 103 { 104 throw new AmetysRepositoryException("Error getting the step ID property.", e); 105 } 106 } 107 108 /** 109 * Set the step ID. 110 * @param stepId the step ID to set. 111 */ 112 public void setStepId(int stepId) 113 { 114 try 115 { 116 _node.setProperty(AbstractJackrabbitWorkflowStore.__STEP_ID_PROPERTY, stepId); 117 } 118 catch (RepositoryException e) 119 { 120 throw new AmetysRepositoryException("Error setting the step ID property.", e); 121 } 122 } 123 124 @Override 125 public int getActionId() 126 { 127 try 128 { 129 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__ACTION_ID_PROPERTY)) 130 { 131 return (int) _node.getProperty(AbstractJackrabbitWorkflowStore.__ACTION_ID_PROPERTY).getLong(); 132 } 133 134 return 0; 135 } 136 catch (RepositoryException e) 137 { 138 throw new AmetysRepositoryException("Error getting the action ID property.", e); 139 } 140 } 141 142 /** 143 * Set the action ID. 144 * @param actionId the action ID to set. 145 */ 146 public void setActionId(int actionId) 147 { 148 try 149 { 150 _node.setProperty(AbstractJackrabbitWorkflowStore.__ACTION_ID_PROPERTY, actionId); 151 } 152 catch (RepositoryException e) 153 { 154 throw new AmetysRepositoryException("Error setting the action ID property.", e); 155 } 156 } 157 158 @Override 159 public String getCaller() 160 { 161 try 162 { 163 String caller = null; 164 165 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__CALLER_PROPERTY)) 166 { 167 caller = _node.getProperty(AbstractJackrabbitWorkflowStore.__CALLER_PROPERTY).getString(); 168 } 169 170 return caller; 171 } 172 catch (RepositoryException e) 173 { 174 throw new AmetysRepositoryException("Error getting the caller property.", e); 175 } 176 } 177 178 /** 179 * Set the caller. 180 * @param caller the caller to set. 181 */ 182 public void setCaller(String caller) 183 { 184 try 185 { 186 _node.setProperty(AbstractJackrabbitWorkflowStore.__CALLER_PROPERTY, caller); 187 } 188 catch (RepositoryException e) 189 { 190 throw new AmetysRepositoryException("Error setting the caller property.", e); 191 } 192 } 193 194 @Override 195 public Date getStartDate() 196 { 197 try 198 { 199 Date value = null; 200 201 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__START_DATE_PROPERTY)) 202 { 203 value = _node.getProperty(AbstractJackrabbitWorkflowStore.__START_DATE_PROPERTY).getDate().getTime(); 204 } 205 206 return value; 207 } 208 catch (RepositoryException e) 209 { 210 throw new AmetysRepositoryException("Error getting the start date property.", e); 211 } 212 } 213 214 /** 215 * Set the step start date. 216 * @param startDate the start date to set. 217 */ 218 public void setStartDate(Date startDate) 219 { 220 try 221 { 222 GregorianCalendar cal = new GregorianCalendar(); 223 cal.setTime(startDate); 224 225 _node.setProperty(AbstractJackrabbitWorkflowStore.__START_DATE_PROPERTY, cal); 226 } 227 catch (RepositoryException e) 228 { 229 throw new AmetysRepositoryException("Error setting the start date property.", e); 230 } 231 } 232 233 @Override 234 public Date getDueDate() 235 { 236 try 237 { 238 Date value = null; 239 240 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__DUE_DATE_PROPERTY)) 241 { 242 value = _node.getProperty(AbstractJackrabbitWorkflowStore.__DUE_DATE_PROPERTY).getDate().getTime(); 243 } 244 245 return value; 246 } 247 catch (RepositoryException e) 248 { 249 throw new AmetysRepositoryException("Error getting the due date property.", e); 250 } 251 } 252 253 /** 254 * Set the step due date. 255 * @param dueDate the due date to set. 256 */ 257 public void setDueDate(Date dueDate) 258 { 259 try 260 { 261 GregorianCalendar cal = new GregorianCalendar(); 262 cal.setTime(dueDate); 263 264 _node.setProperty(AbstractJackrabbitWorkflowStore.__DUE_DATE_PROPERTY, cal); 265 } 266 catch (RepositoryException e) 267 { 268 throw new AmetysRepositoryException("Error setting the due date property.", e); 269 } 270 } 271 272 @Override 273 public Date getFinishDate() 274 { 275 try 276 { 277 Date value = null; 278 279 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__FINISH_DATE_PROPERTY)) 280 { 281 value = _node.getProperty(AbstractJackrabbitWorkflowStore.__FINISH_DATE_PROPERTY).getDate().getTime(); 282 } 283 284 return value; 285 } 286 catch (RepositoryException e) 287 { 288 throw new AmetysRepositoryException("Error getting the finish date property.", e); 289 } 290 } 291 292 /** 293 * Set the step finish date. 294 * @param finishDate the finish date to set. 295 */ 296 public void setFinishDate(Date finishDate) 297 { 298 try 299 { 300 GregorianCalendar cal = new GregorianCalendar(); 301 cal.setTime(finishDate); 302 303 _node.setProperty(AbstractJackrabbitWorkflowStore.__FINISH_DATE_PROPERTY, cal); 304 } 305 catch (RepositoryException e) 306 { 307 throw new AmetysRepositoryException("Error setting the finish date property.", e); 308 } 309 } 310 311 @Override 312 public String getOwner() 313 { 314 try 315 { 316 String owner = null; 317 318 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__OWNER_PROPERTY)) 319 { 320 owner = _node.getProperty(AbstractJackrabbitWorkflowStore.__OWNER_PROPERTY).getString(); 321 } 322 323 return owner; 324 } 325 catch (RepositoryException e) 326 { 327 throw new AmetysRepositoryException("Error getting the owner property.", e); 328 } 329 } 330 331 /** 332 * Set the owner. 333 * @param owner the owner to set. 334 */ 335 public void setOwner(String owner) 336 { 337 try 338 { 339 _node.setProperty(AbstractJackrabbitWorkflowStore.__OWNER_PROPERTY, owner); 340 } 341 catch (RepositoryException e) 342 { 343 throw new AmetysRepositoryException("Error setting the owner property.", e); 344 } 345 } 346 347 @Override 348 public long[] getPreviousStepIds() 349 { 350 try 351 { 352 List<Long> previousStepsIds = new ArrayList<>(); 353 354 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__PREVIOUS_STEPS_PROPERTY)) 355 { 356 Value[] previousSteps = _node.getProperty(AbstractJackrabbitWorkflowStore.__PREVIOUS_STEPS_PROPERTY).getValues(); 357 358 for (Value previousStep : previousSteps) 359 { 360 Node historyStep = _node.getSession().getNodeByIdentifier(previousStep.getString()); 361 362 long previousStepId = historyStep.getProperty(AbstractJackrabbitWorkflowStore.__ID_PROPERTY).getLong(); 363 previousStepsIds.add(previousStepId); 364 } 365 } 366 367 long[] previousIds = new long[previousStepsIds.size()]; 368 369 for (int i = 0; i < previousIds.length; i++) 370 { 371 previousIds[i] = previousStepsIds.get(i); 372 } 373 374 return previousIds; 375 } 376 catch (RepositoryException e) 377 { 378 throw new AmetysRepositoryException("Error getting the previous IDs property.", e); 379 } 380 } 381 382 /** 383 * Set the previous step IDs. 384 * @param previousStepIds the previous step IDs to set. 385 */ 386 public void setPreviousStepIds(long[] previousStepIds) 387 { 388 try 389 { 390 Session session = _node.getSession(); 391 ValueFactory valueFactory = session.getValueFactory(); 392 393 Value[] previousStepsRefs = new Value[0]; 394 395 if (previousStepIds != null) 396 { 397 previousStepsRefs = new Value[previousStepIds.length]; 398 399 for (int i = 0; i < previousStepIds.length; i++) 400 { 401 long previousStepId = previousStepIds[i]; 402 403 // Retrieve existing step for this entry 404 Node previousStep = _store.getHistoryStepNode(session, getEntryId(), previousStepId); 405 406 // Use the ValueFactory to set a reference to this step 407 previousStepsRefs[i] = valueFactory.createValue(previousStep); 408 } 409 } 410 411 _node.setProperty(AbstractJackrabbitWorkflowStore.__PREVIOUS_STEPS_PROPERTY, previousStepsRefs); 412 } 413 catch (RepositoryException e) 414 { 415 throw new AmetysRepositoryException("Error setting the previous IDs property.", e); 416 } 417 } 418 419 @Override 420 public String getStatus() 421 { 422 try 423 { 424 String status = null; 425 426 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__STATUS_PROPERTY)) 427 { 428 status = _node.getProperty(AbstractJackrabbitWorkflowStore.__STATUS_PROPERTY).getString(); 429 } 430 431 return status; 432 } 433 catch (RepositoryException e) 434 { 435 throw new AmetysRepositoryException("Error getting the status property.", e); 436 } 437 } 438 439 /** 440 * Set the status. 441 * @param status the status to set. 442 */ 443 public void setStatus(String status) 444 { 445 try 446 { 447 _node.setProperty(AbstractJackrabbitWorkflowStore.__STATUS_PROPERTY, status); 448 } 449 catch (RepositoryException e) 450 { 451 throw new AmetysRepositoryException("Error setting the status property.", e); 452 } 453 } 454 455 /** 456 * Get a custom property value. 457 * @param name the property name. 458 * @return the custom property value. 459 */ 460 public Object getProperty(String name) 461 { 462 try 463 { 464 Object value = null; 465 String qName = __CUSTOM_PROP_PREFIX + name; 466 467 if (_node.hasProperty(qName)) 468 { 469 Property property = _node.getProperty(qName); 470 if (property.getDefinition().isMultiple()) 471 { 472 value = getMultiValuedProperty(property); 473 } 474 else 475 { 476 value = getSingleValuedProperty(property); 477 } 478 } 479 480 return value; 481 } 482 catch (RepositoryException e) 483 { 484 throw new AmetysRepositoryException("Error setting the custom property: " + name, e); 485 } 486 } 487 488 /** 489 * Set a custom property value. 490 * @param name the property name. 491 * @param value the value to set. 492 */ 493 public void setProperty(String name, Object value) 494 { 495 try 496 { 497 ValueFactory valueFactory = _node.getSession().getValueFactory(); 498 String qName = __CUSTOM_PROP_PREFIX + name; 499 500 if (value instanceof Boolean) 501 { 502 _node.setProperty(qName, (Boolean) value); 503 } 504 else if (value instanceof boolean[]) 505 { 506 boolean[] v = (boolean[]) value; 507 Value[] values = new Value[v.length]; 508 for (int i = 0; i < v.length; i++) 509 { 510 values[i] = valueFactory.createValue(v[i]); 511 } 512 513 _node.setProperty(qName, values); 514 } 515 else if (value instanceof Date) 516 { 517 GregorianCalendar gc = new GregorianCalendar(); 518 gc.setTime((Date) value); 519 _node.setProperty(qName, gc); 520 } 521 else if (value instanceof Date[]) 522 { 523 Date[] v = (Date[]) value; 524 Value[] values = new Value[v.length]; 525 for (int i = 0; i < v.length; i++) 526 { 527 GregorianCalendar gc = new GregorianCalendar(); 528 gc.setTime(v[i]); 529 values[i] = valueFactory.createValue(gc); 530 } 531 532 _node.setProperty(qName, values); 533 } 534 else if (value instanceof Long) 535 { 536 _node.setProperty(qName, (Long) value); 537 } 538 else if (value instanceof long[]) 539 { 540 long[] v = (long[]) value; 541 Value[] values = new Value[v.length]; 542 for (int i = 0; i < v.length; i++) 543 { 544 values[i] = valueFactory.createValue(v[i]); 545 } 546 547 _node.setProperty(qName, values); 548 } 549 else if (value instanceof String[]) 550 { 551 String[] v = (String[]) value; 552 Value[] values = new Value[v.length]; 553 for (int i = 0; i < v.length; i++) 554 { 555 values[i] = valueFactory.createValue(v[i]); 556 } 557 558 _node.setProperty(qName, values); 559 } 560 else 561 { 562 _node.setProperty(qName, value.toString()); 563 } 564 } 565 catch (RepositoryException e) 566 { 567 throw new AmetysRepositoryException("Error setting the custom property: " + name, e); 568 } 569 } 570 571 /** 572 * Get all the custom property names. 573 * @return the custom property names. 574 */ 575 public Set<String> getPropertyNames() 576 { 577 try 578 { 579 Set<String> propertyNames = new LinkedHashSet<>(); 580 581 PropertyIterator properties = _node.getProperties(__CUSTOM_PROP_PREFIX + "*"); 582 while (properties.hasNext()) 583 { 584 String qName = properties.nextProperty().getName(); 585 String name = qName.substring(__CUSTOM_PROP_PREFIX.length()); 586 propertyNames.add(name); 587 } 588 589 return propertyNames; 590 } 591 catch (RepositoryException e) 592 { 593 throw new AmetysRepositoryException("Error retrieving the custom property list.", e); 594 } 595 } 596 597 /** 598 * Save the step. 599 */ 600 public void save() 601 { 602 try 603 { 604 _node.getSession().save(); 605 } 606 catch (RepositoryException e) 607 { 608 throw new AmetysRepositoryException("Error saving the .", e); 609 } 610 } 611 612 /** 613 * Get a single-valued property. 614 * @param property the JCR property. 615 * @return the property value. 616 * @throws RepositoryException if an error occurs. 617 */ 618 protected Object getSingleValuedProperty(Property property) throws RepositoryException 619 { 620 Object value; 621 622 switch (property.getType()) 623 { 624 case PropertyType.BOOLEAN: 625 value = property.getBoolean(); 626 break; 627 case PropertyType.DATE: 628 value = property.getDate().getTime(); 629 break; 630 case PropertyType.LONG: 631 value = property.getLong(); 632 break; 633 default: 634 value = property.getString(); 635 break; 636 } 637 638 return value; 639 } 640 641 /** 642 * Get a multi-valued property. 643 * @param property the JCR property. 644 * @return the property value (as an array). 645 * @throws RepositoryException if an error occurs. 646 */ 647 protected Object getMultiValuedProperty(Property property) throws RepositoryException 648 { 649 Object value; 650 Value[] values = property.getValues(); 651 652 switch (property.getType()) 653 { 654 case PropertyType.BOOLEAN: 655 boolean[] booleanResults = new boolean[values.length]; 656 657 for (int i = 0; i < values.length; i++) 658 { 659 booleanResults[i] = values[i].getBoolean(); 660 } 661 662 value = booleanResults; 663 break; 664 case PropertyType.DATE: 665 Date[] dateResults = new Date[values.length]; 666 667 for (int i = 0; i < values.length; i++) 668 { 669 dateResults[i] = values[i].getDate().getTime(); 670 } 671 672 value = dateResults; 673 break; 674 case PropertyType.LONG: 675 long[] longResults = new long[values.length]; 676 677 for (int i = 0; i < values.length; i++) 678 { 679 longResults[i] = values[i].getLong(); 680 } 681 682 value = longResults; 683 break; 684 default: 685 String[] stringResults = new String[values.length]; 686 687 for (int i = 0; i < values.length; i++) 688 { 689 stringResults[i] = values[i].getString(); 690 } 691 692 value = stringResults; 693 break; 694 } 695 696 return value; 697 } 698 699 @Override 700 public String toString() 701 { 702 return "AmetysStep (" + getId() + ")"; 703 } 704 705}