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 return (int) _node.getProperty(AbstractJackrabbitWorkflowStore.__ACTION_ID_PROPERTY).getLong(); 130 } 131 catch (RepositoryException e) 132 { 133 throw new AmetysRepositoryException("Error getting the action ID property.", e); 134 } 135 } 136 137 /** 138 * Set the action ID. 139 * @param actionId the action ID to set. 140 */ 141 public void setActionId(int actionId) 142 { 143 try 144 { 145 _node.setProperty(AbstractJackrabbitWorkflowStore.__ACTION_ID_PROPERTY, actionId); 146 } 147 catch (RepositoryException e) 148 { 149 throw new AmetysRepositoryException("Error setting the action ID property.", e); 150 } 151 } 152 153 @Override 154 public String getCaller() 155 { 156 try 157 { 158 String caller = null; 159 160 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__CALLER_PROPERTY)) 161 { 162 caller = _node.getProperty(AbstractJackrabbitWorkflowStore.__CALLER_PROPERTY).getString(); 163 } 164 165 return caller; 166 } 167 catch (RepositoryException e) 168 { 169 throw new AmetysRepositoryException("Error getting the caller property.", e); 170 } 171 } 172 173 /** 174 * Set the caller. 175 * @param caller the caller to set. 176 */ 177 public void setCaller(String caller) 178 { 179 try 180 { 181 _node.setProperty(AbstractJackrabbitWorkflowStore.__CALLER_PROPERTY, caller); 182 } 183 catch (RepositoryException e) 184 { 185 throw new AmetysRepositoryException("Error setting the caller property.", e); 186 } 187 } 188 189 @Override 190 public Date getStartDate() 191 { 192 try 193 { 194 Date value = null; 195 196 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__START_DATE_PROPERTY)) 197 { 198 value = _node.getProperty(AbstractJackrabbitWorkflowStore.__START_DATE_PROPERTY).getDate().getTime(); 199 } 200 201 return value; 202 } 203 catch (RepositoryException e) 204 { 205 throw new AmetysRepositoryException("Error getting the start date property.", e); 206 } 207 } 208 209 /** 210 * Set the step start date. 211 * @param startDate the start date to set. 212 */ 213 public void setStartDate(Date startDate) 214 { 215 try 216 { 217 GregorianCalendar cal = new GregorianCalendar(); 218 cal.setTime(startDate); 219 220 _node.setProperty(AbstractJackrabbitWorkflowStore.__START_DATE_PROPERTY, cal); 221 } 222 catch (RepositoryException e) 223 { 224 throw new AmetysRepositoryException("Error setting the start date property.", e); 225 } 226 } 227 228 @Override 229 public Date getDueDate() 230 { 231 try 232 { 233 Date value = null; 234 235 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__DUE_DATE_PROPERTY)) 236 { 237 value = _node.getProperty(AbstractJackrabbitWorkflowStore.__DUE_DATE_PROPERTY).getDate().getTime(); 238 } 239 240 return value; 241 } 242 catch (RepositoryException e) 243 { 244 throw new AmetysRepositoryException("Error getting the due date property.", e); 245 } 246 } 247 248 /** 249 * Set the step due date. 250 * @param dueDate the due date to set. 251 */ 252 public void setDueDate(Date dueDate) 253 { 254 try 255 { 256 GregorianCalendar cal = new GregorianCalendar(); 257 cal.setTime(dueDate); 258 259 _node.setProperty(AbstractJackrabbitWorkflowStore.__DUE_DATE_PROPERTY, cal); 260 } 261 catch (RepositoryException e) 262 { 263 throw new AmetysRepositoryException("Error setting the due date property.", e); 264 } 265 } 266 267 @Override 268 public Date getFinishDate() 269 { 270 try 271 { 272 Date value = null; 273 274 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__FINISH_DATE_PROPERTY)) 275 { 276 value = _node.getProperty(AbstractJackrabbitWorkflowStore.__FINISH_DATE_PROPERTY).getDate().getTime(); 277 } 278 279 return value; 280 } 281 catch (RepositoryException e) 282 { 283 throw new AmetysRepositoryException("Error getting the finish date property.", e); 284 } 285 } 286 287 /** 288 * Set the step finish date. 289 * @param finishDate the finish date to set. 290 */ 291 public void setFinishDate(Date finishDate) 292 { 293 try 294 { 295 GregorianCalendar cal = new GregorianCalendar(); 296 cal.setTime(finishDate); 297 298 _node.setProperty(AbstractJackrabbitWorkflowStore.__FINISH_DATE_PROPERTY, cal); 299 } 300 catch (RepositoryException e) 301 { 302 throw new AmetysRepositoryException("Error setting the finish date property.", e); 303 } 304 } 305 306 @Override 307 public String getOwner() 308 { 309 try 310 { 311 String owner = null; 312 313 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__OWNER_PROPERTY)) 314 { 315 owner = _node.getProperty(AbstractJackrabbitWorkflowStore.__OWNER_PROPERTY).getString(); 316 } 317 318 return owner; 319 } 320 catch (RepositoryException e) 321 { 322 throw new AmetysRepositoryException("Error getting the owner property.", e); 323 } 324 } 325 326 /** 327 * Set the owner. 328 * @param owner the owner to set. 329 */ 330 public void setOwner(String owner) 331 { 332 try 333 { 334 _node.setProperty(AbstractJackrabbitWorkflowStore.__OWNER_PROPERTY, owner); 335 } 336 catch (RepositoryException e) 337 { 338 throw new AmetysRepositoryException("Error setting the owner property.", e); 339 } 340 } 341 342 @Override 343 public long[] getPreviousStepIds() 344 { 345 try 346 { 347 List<Long> previousStepsIds = new ArrayList<>(); 348 349 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__PREVIOUS_STEPS_PROPERTY)) 350 { 351 Value[] previousSteps = _node.getProperty(AbstractJackrabbitWorkflowStore.__PREVIOUS_STEPS_PROPERTY).getValues(); 352 353 for (Value previousStep : previousSteps) 354 { 355 Node historyStep = _node.getSession().getNodeByIdentifier(previousStep.getString()); 356 357 long previousStepId = historyStep.getProperty(AbstractJackrabbitWorkflowStore.__ID_PROPERTY).getLong(); 358 previousStepsIds.add(previousStepId); 359 } 360 } 361 362 long[] previousIds = new long[previousStepsIds.size()]; 363 364 for (int i = 0; i < previousIds.length; i++) 365 { 366 previousIds[i] = previousStepsIds.get(i); 367 } 368 369 return previousIds; 370 } 371 catch (RepositoryException e) 372 { 373 throw new AmetysRepositoryException("Error getting the previous IDs property.", e); 374 } 375 } 376 377 /** 378 * Set the previous step IDs. 379 * @param previousStepIds the previous step IDs to set. 380 */ 381 public void setPreviousStepIds(long[] previousStepIds) 382 { 383 try 384 { 385 Session session = _node.getSession(); 386 ValueFactory valueFactory = session.getValueFactory(); 387 388 Value[] previousStepsRefs = new Value[0]; 389 390 if (previousStepIds != null) 391 { 392 previousStepsRefs = new Value[previousStepIds.length]; 393 394 for (int i = 0; i < previousStepIds.length; i++) 395 { 396 long previousStepId = previousStepIds[i]; 397 398 // Retrieve existing step for this entry 399 Node previousStep = _store.getHistoryStepNode(session, getEntryId(), previousStepId); 400 401 // Use the ValueFactory to set a reference to this step 402 previousStepsRefs[i] = valueFactory.createValue(previousStep); 403 } 404 } 405 406 _node.setProperty(AbstractJackrabbitWorkflowStore.__PREVIOUS_STEPS_PROPERTY, previousStepsRefs); 407 } 408 catch (RepositoryException e) 409 { 410 throw new AmetysRepositoryException("Error setting the previous IDs property.", e); 411 } 412 } 413 414 @Override 415 public String getStatus() 416 { 417 try 418 { 419 String status = null; 420 421 if (_node.hasProperty(AbstractJackrabbitWorkflowStore.__STATUS_PROPERTY)) 422 { 423 status = _node.getProperty(AbstractJackrabbitWorkflowStore.__STATUS_PROPERTY).getString(); 424 } 425 426 return status; 427 } 428 catch (RepositoryException e) 429 { 430 throw new AmetysRepositoryException("Error getting the status property.", e); 431 } 432 } 433 434 /** 435 * Set the status. 436 * @param status the status to set. 437 */ 438 public void setStatus(String status) 439 { 440 try 441 { 442 _node.setProperty(AbstractJackrabbitWorkflowStore.__STATUS_PROPERTY, status); 443 } 444 catch (RepositoryException e) 445 { 446 throw new AmetysRepositoryException("Error setting the status property.", e); 447 } 448 } 449 450 /** 451 * Get a custom property value. 452 * @param name the property name. 453 * @return the custom property value. 454 */ 455 public Object getProperty(String name) 456 { 457 try 458 { 459 Object value = null; 460 String qName = __CUSTOM_PROP_PREFIX + name; 461 462 if (_node.hasProperty(qName)) 463 { 464 Property property = _node.getProperty(qName); 465 if (property.getDefinition().isMultiple()) 466 { 467 value = getMultiValuedProperty(property); 468 } 469 else 470 { 471 value = getSingleValuedProperty(property); 472 } 473 } 474 475 return value; 476 } 477 catch (RepositoryException e) 478 { 479 throw new AmetysRepositoryException("Error setting the custom property: " + name, e); 480 } 481 } 482 483 /** 484 * Set a custom property value. 485 * @param name the property name. 486 * @param value the value to set. 487 */ 488 public void setProperty(String name, Object value) 489 { 490 try 491 { 492 ValueFactory valueFactory = _node.getSession().getValueFactory(); 493 String qName = __CUSTOM_PROP_PREFIX + name; 494 495 if (value instanceof Boolean) 496 { 497 _node.setProperty(qName, (Boolean) value); 498 } 499 else if (value instanceof boolean[]) 500 { 501 boolean[] v = (boolean[]) value; 502 Value[] values = new Value[v.length]; 503 for (int i = 0; i < v.length; i++) 504 { 505 values[i] = valueFactory.createValue(v[i]); 506 } 507 508 _node.setProperty(qName, values); 509 } 510 else if (value instanceof Date) 511 { 512 GregorianCalendar gc = new GregorianCalendar(); 513 gc.setTime((Date) value); 514 _node.setProperty(qName, gc); 515 } 516 else if (value instanceof Date[]) 517 { 518 Date[] v = (Date[]) value; 519 Value[] values = new Value[v.length]; 520 for (int i = 0; i < v.length; i++) 521 { 522 GregorianCalendar gc = new GregorianCalendar(); 523 gc.setTime(v[i]); 524 values[i] = valueFactory.createValue(gc); 525 } 526 527 _node.setProperty(qName, values); 528 } 529 else if (value instanceof Long) 530 { 531 _node.setProperty(qName, (Long) value); 532 } 533 else if (value instanceof long[]) 534 { 535 long[] v = (long[]) value; 536 Value[] values = new Value[v.length]; 537 for (int i = 0; i < v.length; i++) 538 { 539 values[i] = valueFactory.createValue(v[i]); 540 } 541 542 _node.setProperty(qName, values); 543 } 544 else if (value instanceof String[]) 545 { 546 String[] v = (String[]) value; 547 Value[] values = new Value[v.length]; 548 for (int i = 0; i < v.length; i++) 549 { 550 values[i] = valueFactory.createValue(v[i]); 551 } 552 553 _node.setProperty(qName, values); 554 } 555 else 556 { 557 _node.setProperty(qName, value.toString()); 558 } 559 } 560 catch (RepositoryException e) 561 { 562 throw new AmetysRepositoryException("Error setting the custom property: " + name, e); 563 } 564 } 565 566 /** 567 * Get all the custom property names. 568 * @return the custom property names. 569 */ 570 public Set<String> getPropertyNames() 571 { 572 try 573 { 574 Set<String> propertyNames = new LinkedHashSet<>(); 575 576 PropertyIterator properties = _node.getProperties(__CUSTOM_PROP_PREFIX + "*"); 577 while (properties.hasNext()) 578 { 579 String qName = properties.nextProperty().getName(); 580 String name = qName.substring(__CUSTOM_PROP_PREFIX.length()); 581 propertyNames.add(name); 582 } 583 584 return propertyNames; 585 } 586 catch (RepositoryException e) 587 { 588 throw new AmetysRepositoryException("Error retrieving the custom property list.", e); 589 } 590 } 591 592 /** 593 * Save the step. 594 */ 595 public void save() 596 { 597 try 598 { 599 _node.getSession().save(); 600 } 601 catch (RepositoryException e) 602 { 603 throw new AmetysRepositoryException("Error saving the .", e); 604 } 605 } 606 607 /** 608 * Get a single-valued property. 609 * @param property the JCR property. 610 * @return the property value. 611 * @throws RepositoryException if an error occurs. 612 */ 613 protected Object getSingleValuedProperty(Property property) throws RepositoryException 614 { 615 Object value; 616 617 switch (property.getType()) 618 { 619 case PropertyType.BOOLEAN: 620 value = property.getBoolean(); 621 break; 622 case PropertyType.DATE: 623 value = property.getDate().getTime(); 624 break; 625 case PropertyType.LONG: 626 value = property.getLong(); 627 break; 628 default: 629 value = property.getString(); 630 break; 631 } 632 633 return value; 634 } 635 636 /** 637 * Get a multi-valued property. 638 * @param property the JCR property. 639 * @return the property value (as an array). 640 * @throws RepositoryException if an error occurs. 641 */ 642 protected Object getMultiValuedProperty(Property property) throws RepositoryException 643 { 644 Object value; 645 Value[] values = property.getValues(); 646 647 switch (property.getType()) 648 { 649 case PropertyType.BOOLEAN: 650 boolean[] booleanResults = new boolean[values.length]; 651 652 for (int i = 0; i < values.length; i++) 653 { 654 booleanResults[i] = values[i].getBoolean(); 655 } 656 657 value = booleanResults; 658 break; 659 case PropertyType.DATE: 660 Date[] dateResults = new Date[values.length]; 661 662 for (int i = 0; i < values.length; i++) 663 { 664 dateResults[i] = values[i].getDate().getTime(); 665 } 666 667 value = dateResults; 668 break; 669 case PropertyType.LONG: 670 long[] longResults = new long[values.length]; 671 672 for (int i = 0; i < values.length; i++) 673 { 674 longResults[i] = values[i].getLong(); 675 } 676 677 value = longResults; 678 break; 679 default: 680 String[] stringResults = new String[values.length]; 681 682 for (int i = 0; i < values.length; i++) 683 { 684 stringResults[i] = values[i].getString(); 685 } 686 687 value = stringResults; 688 break; 689 } 690 691 return value; 692 } 693 694 @Override 695 public String toString() 696 { 697 return "AmetysStep (" + getId() + ")"; 698 } 699 700}