001/* 002 * Copyright 2018 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.repository.data.repositorydata.impl; 017 018import java.io.InputStream; 019import java.util.ArrayList; 020import java.util.Calendar; 021import java.util.Date; 022import java.util.GregorianCalendar; 023import java.util.LinkedHashMap; 024import java.util.LinkedHashSet; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028 029import javax.jcr.Binary; 030import javax.jcr.Node; 031import javax.jcr.NodeIterator; 032import javax.jcr.PathNotFoundException; 033import javax.jcr.Property; 034import javax.jcr.PropertyIterator; 035import javax.jcr.PropertyType; 036import javax.jcr.RepositoryException; 037import javax.jcr.Session; 038import javax.jcr.Value; 039import javax.jcr.lock.Lock; 040import javax.jcr.lock.LockManager; 041 042import org.apache.commons.lang3.StringUtils; 043 044import org.ametys.plugins.repository.AmetysObjectResolver; 045import org.ametys.plugins.repository.AmetysRepositoryException; 046import org.ametys.plugins.repository.RepositoryConstants; 047import org.ametys.plugins.repository.data.DataComment; 048import org.ametys.plugins.repository.data.UnknownDataException; 049import org.ametys.plugins.repository.data.repositorydata.ModifiableCommentableRepositoryData; 050import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 051import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 052import org.ametys.plugins.repository.jcr.NodeHelper; 053import org.ametys.plugins.repository.jcr.NodeTypeHelper; 054 055/** 056 * Class for data values management in JCR repository 057 */ 058public class JCRRepositoryData implements ModifiableRepositoryData, ModifiableCommentableRepositoryData 059{ 060 private static final Map<Integer, String> __DATA_TYPES; 061 062 static 063 { 064 __DATA_TYPES = new LinkedHashMap<>(6); 065 __DATA_TYPES.put(PropertyType.STRING, RepositoryData.STRING_REPOSITORY_DATA_TYPE); 066 __DATA_TYPES.put(PropertyType.LONG, RepositoryData.LONG_REPOSITORY_DATA_TYPE); 067 __DATA_TYPES.put(PropertyType.DOUBLE, RepositoryData.DOUBLE_REPOSITORY_DATA_TYPE); 068 __DATA_TYPES.put(PropertyType.BOOLEAN, RepositoryData.BOOLEAN_REPOSITORY_DATA_TYPE); 069 __DATA_TYPES.put(PropertyType.DATE, RepositoryData.CALENDAR_REPOSITORY_DATA_TYPE); 070 __DATA_TYPES.put(PropertyType.BINARY, RepositoryData.STREAM_REPOSITORY_DATA_TYPE); 071 } 072 073 private Node _node; 074 private boolean _lockAlreadyChecked; 075 private String _defaultPrefix; 076 077 /** 078 * Creates a JCR repository data from the given node 079 * @param node the Node supporting this repository data 080 */ 081 public JCRRepositoryData(Node node) 082 { 083 this(node, RepositoryConstants.NAMESPACE_PREFIX); 084 } 085 086 /** 087 * Creates a JCR repository data from the given node 088 * @param node the Node supporting this repository data 089 * @param defaultPrefix prefix to use for properties and child nodes 090 */ 091 public JCRRepositoryData(Node node, String defaultPrefix) 092 { 093 _node = node; 094 _defaultPrefix = defaultPrefix; 095 } 096 097 public String getString(String name, String prefix) 098 { 099 try 100 { 101 Value value = _getValue(name, prefix); 102 return value.getString(); 103 } 104 catch (RepositoryException e) 105 { 106 throw new AmetysRepositoryException("Unable to get the value of string data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 107 } 108 } 109 110 public String[] getStrings(String name, String prefix) 111 { 112 try 113 { 114 Value[] values = _getValues(name, prefix); 115 String[] results = new String[values.length]; 116 117 for (int i = 0; i < values.length; i++) 118 { 119 Value value = values[i]; 120 results[i] = value.getString(); 121 } 122 123 return results; 124 } 125 catch (RepositoryException e) 126 { 127 throw new AmetysRepositoryException("Unable to get the values of multiple string data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 128 } 129 } 130 131 public Calendar getDate(String name, String prefix) 132 { 133 try 134 { 135 Value value = _getValue(name, prefix); 136 return value.getDate(); 137 } 138 catch (RepositoryException e) 139 { 140 throw new AmetysRepositoryException("Unable to get the value of date data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 141 } 142 } 143 144 public Calendar[] getDates(String name, String prefix) 145 { 146 try 147 { 148 Value[] values = _getValues(name, prefix); 149 Calendar[] results = new Calendar[values.length]; 150 151 for (int i = 0; i < values.length; i++) 152 { 153 Value value = values[i]; 154 results[i] = value.getDate(); 155 } 156 157 return results; 158 } 159 catch (RepositoryException e) 160 { 161 throw new AmetysRepositoryException("Unable to get the values of multiple date data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 162 } 163 } 164 165 public Long getLong(String name, String prefix) 166 { 167 try 168 { 169 Value value = _getValue(name, prefix); 170 return value.getLong(); 171 } 172 catch (RepositoryException e) 173 { 174 throw new AmetysRepositoryException("Unable to get the value on long data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 175 } 176 } 177 178 public Long[] getLongs(String name, String prefix) 179 { 180 try 181 { 182 Value[] values = _getValues(name, prefix); 183 Long[] results = new Long[values.length]; 184 185 for (int i = 0; i < values.length; i++) 186 { 187 Value value = values[i]; 188 results[i] = value.getLong(); 189 } 190 191 return results; 192 } 193 catch (RepositoryException e) 194 { 195 throw new AmetysRepositoryException("Unable to get the values of multiple long data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 196 } 197 } 198 199 public Double getDouble(String name, String prefix) 200 { 201 try 202 { 203 Value value = _getValue(name, prefix); 204 return value.getDouble(); 205 } 206 catch (RepositoryException e) 207 { 208 throw new AmetysRepositoryException("Unable to get the value of double data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 209 } 210 } 211 212 public Double[] getDoubles(String name, String prefix) 213 { 214 try 215 { 216 Value[] values = _getValues(name, prefix); 217 Double[] results = new Double[values.length]; 218 219 for (int i = 0; i < values.length; i++) 220 { 221 Value value = values[i]; 222 results[i] = value.getDouble(); 223 } 224 225 return results; 226 } 227 catch (RepositoryException e) 228 { 229 throw new AmetysRepositoryException("Unable to get the values of multiple double data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 230 } 231 } 232 233 public Boolean getBoolean(String name, String prefix) 234 { 235 try 236 { 237 Value value = _getValue(name, prefix); 238 return value.getBoolean(); 239 } 240 catch (RepositoryException e) 241 { 242 throw new AmetysRepositoryException("Unable to get the value of boolean data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 243 } 244 } 245 246 public Boolean[] getBooleans(String name, String prefix) 247 { 248 try 249 { 250 Value[] values = _getValues(name, prefix); 251 Boolean[] results = new Boolean[values.length]; 252 253 for (int i = 0; i < values.length; i++) 254 { 255 Value value = values[i]; 256 results[i] = value.getBoolean(); 257 } 258 259 return results; 260 } 261 catch (RepositoryException e) 262 { 263 throw new AmetysRepositoryException("Unable to get the values of multiple boolean data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 264 } 265 } 266 267 public InputStream[] getStreams(String name, String prefix) 268 { 269 try 270 { 271 Value[] values = _getValues(name, prefix); 272 InputStream[] results = new InputStream[values.length]; 273 274 for (int i = 0; i < values.length; i++) 275 { 276 Value value = values[i]; 277 results[i] = value.getBinary().getStream(); 278 } 279 280 return results; 281 } 282 catch (RepositoryException e) 283 { 284 throw new AmetysRepositoryException("Unable to get the values of multiple stream data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 285 } 286 } 287 288 public InputStream getStream(String name, String prefix) 289 { 290 try 291 { 292 Value value = _getValue(name, prefix); 293 return value.getBinary().getStream(); 294 } 295 catch (RepositoryException e) 296 { 297 throw new AmetysRepositoryException("Unable to get the value of stream data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 298 } 299 } 300 301 public Long getStreamLength(String name, String prefix) 302 { 303 try 304 { 305 return _node.getProperty(_getFullName(name, prefix)).getLength(); 306 } 307 catch (RepositoryException e) 308 { 309 throw new AmetysRepositoryException("Unable to get the length of the value of stream data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 310 } 311 } 312 313 private Value _getValue(String name, String prefix) 314 { 315 try 316 { 317 Property property = _node.getProperty(_getFullName(name, prefix)); 318 319 if (property.getDefinition().isMultiple()) 320 { 321 Value[] values = property.getValues(); 322 323 if (values.length > 0) 324 { 325 return values[0]; 326 } 327 else 328 { 329 throw new AmetysRepositoryException("Cannot get the value of data '" + _getFullName(name, prefix) + "' from an empty array in repository data at path '" + _getPathForLogs() + "'."); 330 } 331 } 332 333 return property.getValue(); 334 } 335 catch (PathNotFoundException e) 336 { 337 throw new UnknownDataException("Unknown data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 338 } 339 catch (RepositoryException e) 340 { 341 throw new AmetysRepositoryException("Unable to get the value of data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 342 } 343 } 344 345 private Value[] _getValues(String name, String prefix) 346 { 347 try 348 { 349 Property property = _node.getProperty(_getFullName(name, prefix)); 350 351 if (property.getDefinition().isMultiple()) 352 { 353 return property.getValues(); 354 } 355 356 Value value = property.getValue(); 357 return new Value[] {value}; 358 } 359 catch (PathNotFoundException e) 360 { 361 throw new UnknownDataException("Unknown data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 362 } 363 catch (RepositoryException e) 364 { 365 throw new AmetysRepositoryException("Unable to get the multiple value of data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 366 } 367 } 368 369 public ModifiableRepositoryData getRepositoryData(String name, String prefix) 370 { 371 try 372 { 373 if (_node.hasNode(_getFullName(name, prefix))) 374 { 375 Node node = _node.getNode(_getFullName(name, prefix)); 376 return new JCRRepositoryData(node); 377 } 378 else 379 { 380 throw new UnknownDataException("Unknown repository data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'."); 381 } 382 } 383 catch (RepositoryException e) 384 { 385 throw new AmetysRepositoryException("Unable to get the repository data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 386 } 387 } 388 389 public Set<String> getDataNames(String prefix) throws AmetysRepositoryException 390 { 391 final String finalPrefix; 392 if (prefix == null) 393 { 394 finalPrefix = "*:"; 395 } 396 else if (StringUtils.isEmpty(prefix)) 397 { 398 finalPrefix = prefix; 399 } 400 else 401 { 402 finalPrefix = prefix + ":"; 403 } 404 405 try 406 { 407 PropertyIterator propertiesIterator = _node.getProperties(finalPrefix + "*"); 408 NodeIterator itNode = _node.getNodes(finalPrefix + "*"); 409 410 Set<String> names = new LinkedHashSet<>(); 411 412 while (propertiesIterator.hasNext()) 413 { 414 Property property = propertiesIterator.nextProperty(); 415 String propertyName = property.getName(); 416 417 if (!StringUtils.isEmpty(finalPrefix) || !propertyName.contains(":")) 418 { 419 names.add(propertyName.substring(finalPrefix.length())); 420 } 421 } 422 423 while (itNode.hasNext()) 424 { 425 Node node = itNode.nextNode(); 426 427 // Filtering out AmetysObjects: they're not data (except users). 428 if (!NodeTypeHelper.isNodeType(node, AmetysObjectResolver.OBJECT_TYPE) || NodeTypeHelper.isNodeType(node, RepositoryConstants.USER_NODETYPE)) 429 { 430 names.add(node.getName().substring(finalPrefix.length())); 431 } 432 } 433 434 return names; 435 } 436 catch (RepositoryException e) 437 { 438 throw new AmetysRepositoryException("Unable to get the data names of the repository data at path '" + _getPathForLogs() + "'.", e); 439 } 440 } 441 442 public String getFullName() 443 { 444 try 445 { 446 return _node.getName(); 447 } 448 catch (RepositoryException e) 449 { 450 throw new AmetysRepositoryException("Unable to get the name of the repository data at path '" + _getPathForLogs() + "'.", e); 451 } 452 } 453 454 public boolean hasValue(String name, String prefix) 455 { 456 try 457 { 458 return _node.hasProperty(_getFullName(name, prefix)) || _node.hasNode(_getFullName(name, prefix)); 459 } 460 catch (RepositoryException e) 461 { 462 throw new AmetysRepositoryException("Unable to determine if there is a value for data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 463 } 464 } 465 466 public String getType(String name, String prefix) 467 { 468 try 469 { 470 String dataName = _getFullName(name, prefix); 471 472 if (_node.hasProperty(dataName)) 473 { 474 Property property = _node.getProperty(dataName); 475 int jcrType = property.getType(); 476 477 String dataType = __DATA_TYPES.get(jcrType); 478 if (dataType == null) 479 { 480 throw new AmetysRepositoryException("Unable to get the type of data '" + dataName + "' in repository data at path '" + _getPathForLogs() + "'."); 481 } 482 483 return dataType; 484 } 485 486 if (_node.hasNode(dataName)) 487 { 488 return _node.getNode(dataName).getPrimaryNodeType().getName(); 489 } 490 491 throw new UnknownDataException("No data found for '" + dataName + "' in repository data at path '" + _getPathForLogs() + "'."); 492 } 493 catch (RepositoryException e) 494 { 495 throw new AmetysRepositoryException("Unable to get the type of data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 496 } 497 } 498 499 public boolean isMultiple(String name, String prefix) 500 { 501 try 502 { 503 String dataName = _getFullName(name, prefix); 504 505 if (_node.hasProperty(dataName)) 506 { 507 Property property = _node.getProperty(dataName); 508 509 if (property.getDefinition().isMultiple()) 510 { 511 return true; 512 } 513 514 return false; 515 } 516 517 if (_node.hasNode(dataName)) 518 { 519 String nodeName = _node.getNode(dataName).getPrimaryNodeType().getName(); 520 return RepositoryConstants.MULTIPLE_ITEM_NODETYPE.equals(nodeName); 521 } 522 523 throw new UnknownDataException("No data found for '" + dataName + "' in repository data at path '" + _getPathForLogs() + "'."); 524 } 525 catch (RepositoryException e) 526 { 527 throw new AmetysRepositoryException("Unable to determine if data '" + _getFullName(name, prefix) + "' is multiple in repository data at path '" + _getPathForLogs() + "'.", e); 528 } 529 } 530 531 public void rename(String newName, String prefix) 532 { 533 NodeHelper.rename(_node, prefix + ":" + newName); 534 } 535 536 public void setValue(String name, String value, String prefix) 537 { 538 if (value == null) 539 { 540 throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'."); 541 } 542 543 _checkDataName(name, prefix); 544 545 try 546 { 547 _checkLock(); 548 _node.setProperty(_getFullName(name, prefix), value); 549 } 550 catch (RepositoryException e) 551 { 552 throw new AmetysRepositoryException("Unable to set the value '" + value + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 553 } 554 } 555 556 public void setValues(String name, String[] values, String prefix) 557 { 558 if (values == null) 559 { 560 throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'."); 561 } 562 563 _checkDataName(name, prefix); 564 565 try 566 { 567 _checkLock(); 568 _node.setProperty(_getFullName(name, prefix), values); 569 } 570 catch (RepositoryException e) 571 { 572 throw new AmetysRepositoryException("Unable to set the values '" + values + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 573 } 574 } 575 576 public void setValue(String name, Calendar value, String prefix) 577 { 578 if (value == null) 579 { 580 throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'."); 581 } 582 583 _checkDataName(name, prefix); 584 585 try 586 { 587 _checkLock(); 588 _node.setProperty(_getFullName(name, prefix), value); 589 } 590 catch (RepositoryException e) 591 { 592 throw new AmetysRepositoryException("Unable to set the value '" + value + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 593 } 594 } 595 596 public void setValues(String name, Calendar[] values, String prefix) 597 { 598 if (values == null) 599 { 600 throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'."); 601 } 602 603 _checkDataName(name, prefix); 604 605 try 606 { 607 _checkLock(); 608 609 Value[] jcrValues = new Value[values.length]; 610 for (int i = 0; i < values.length; i++) 611 { 612 Calendar value = values[i]; 613 jcrValues[i] = _node.getSession().getValueFactory().createValue(value); 614 } 615 616 _node.setProperty(_getFullName(name, prefix), jcrValues); 617 } 618 catch (RepositoryException e) 619 { 620 throw new AmetysRepositoryException("Unable to set the values '" + values + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 621 } 622 } 623 624 public void setValue(String name, Long value, String prefix) 625 { 626 if (value == null) 627 { 628 throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'."); 629 } 630 631 _checkDataName(name, prefix); 632 633 try 634 { 635 _checkLock(); 636 _node.setProperty(_getFullName(name, prefix), value); 637 } 638 catch (RepositoryException e) 639 { 640 throw new AmetysRepositoryException("Unable to set the value '" + value + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 641 } 642 } 643 644 public void setValues(String name, Long[] values, String prefix) 645 { 646 if (values == null) 647 { 648 throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'."); 649 } 650 651 _checkDataName(name, prefix); 652 653 try 654 { 655 _checkLock(); 656 657 Value[] jcrValues = new Value[values.length]; 658 for (int i = 0; i < values.length; i++) 659 { 660 long value = values[i]; 661 jcrValues[i] = _node.getSession().getValueFactory().createValue(value); 662 } 663 664 _node.setProperty(_getFullName(name, prefix), jcrValues); 665 } 666 catch (RepositoryException e) 667 { 668 throw new AmetysRepositoryException("Unable to set the values '" + values + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 669 } 670 } 671 672 public void setValue(String name, Double value, String prefix) 673 { 674 if (value == null) 675 { 676 throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'."); 677 } 678 679 _checkDataName(name, prefix); 680 681 try 682 { 683 _checkLock(); 684 _node.setProperty(_getFullName(name, prefix), value); 685 } 686 catch (RepositoryException e) 687 { 688 throw new AmetysRepositoryException("Unable to set the value '" + value + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 689 } 690 } 691 692 public void setValues(String name, Double[] values, String prefix) 693 { 694 if (values == null) 695 { 696 throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'."); 697 } 698 699 _checkDataName(name, prefix); 700 701 try 702 { 703 _checkLock(); 704 705 Value[] jcrValues = new Value[values.length]; 706 for (int i = 0; i < values.length; i++) 707 { 708 double value = values[i]; 709 jcrValues[i] = _node.getSession().getValueFactory().createValue(value); 710 } 711 712 _node.setProperty(_getFullName(name, prefix), jcrValues); 713 } 714 catch (RepositoryException e) 715 { 716 throw new AmetysRepositoryException("Unable to set the values '" + values + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 717 } 718 } 719 720 public void setValue(String name, Boolean value, String prefix) 721 { 722 if (value == null) 723 { 724 throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'."); 725 } 726 727 _checkDataName(name, prefix); 728 729 try 730 { 731 _checkLock(); 732 _node.setProperty(_getFullName(name, prefix), value); 733 } 734 catch (RepositoryException e) 735 { 736 throw new AmetysRepositoryException("Unable to set the value '" + value + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 737 } 738 } 739 740 public void setValues(String name, Boolean[] values, String prefix) 741 { 742 if (values == null) 743 { 744 throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'."); 745 } 746 747 _checkDataName(name, prefix); 748 749 try 750 { 751 _checkLock(); 752 753 Value[] jcrValues = new Value[values.length]; 754 for (int i = 0; i < values.length; i++) 755 { 756 boolean value = values[i]; 757 jcrValues[i] = _node.getSession().getValueFactory().createValue(value); 758 } 759 760 _node.setProperty(_getFullName(name, prefix), jcrValues); 761 } 762 catch (RepositoryException e) 763 { 764 throw new AmetysRepositoryException("Unable to set the values '" + values + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 765 } 766 } 767 768 public void setValue(String name, InputStream value, String prefix) 769 { 770 if (value == null) 771 { 772 throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'."); 773 } 774 775 _checkDataName(name, prefix); 776 777 try 778 { 779 _checkLock(); 780 Binary binary = _node.getSession().getValueFactory().createBinary(value); 781 _node.setProperty(_getFullName(name, prefix), binary); 782 } 783 catch (RepositoryException e) 784 { 785 throw new AmetysRepositoryException("Unable to set the value '" + value + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 786 } 787 } 788 789 public void setValues(String name, InputStream[] values, String prefix) 790 { 791 if (values == null) 792 { 793 throw new NullPointerException("Unable to set a 'null' data for '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'."); 794 } 795 796 _checkDataName(name, prefix); 797 798 try 799 { 800 _checkLock(); 801 802 Value[] jcrValues = new Value[values.length]; 803 for (int i = 0; i < values.length; i++) 804 { 805 InputStream value = values[i]; 806 Binary binary = _node.getSession().getValueFactory().createBinary(value); 807 jcrValues[i] = _node.getSession().getValueFactory().createValue(binary); 808 } 809 810 _node.setProperty(_getFullName(name, prefix), jcrValues); 811 } 812 catch (RepositoryException e) 813 { 814 throw new AmetysRepositoryException("Unable to set the values '" + values + "' for data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 815 } 816 } 817 818 public ModifiableRepositoryData addRepositoryData(String name, String dataTypeName, String prefix) 819 { 820 try 821 { 822 _checkLock(); 823 Node node = _node.addNode(_getFullName(name, prefix), dataTypeName); 824 return new JCRRepositoryData(node); 825 } 826 catch (RepositoryException e) 827 { 828 throw new AmetysRepositoryException("Unable to create repository data '" + _getFullName(name, prefix) + "' with type '" + dataTypeName + "' in repository data at path '" + _getPathForLogs() + "'.", e); 829 } 830 } 831 832 private void _checkDataName(String name, String prefix) 833 { 834 // Name null 835 if (name == null) 836 { 837 throw new AmetysRepositoryException("Unable to set a value with the invalid data name (null) in repository data at path '" + _getPathForLogs() + "'."); 838 } 839 840 // Valid name 841 if (!DATA_NAME_PATTERN.matcher(name).matches()) 842 { 843 throw new AmetysRepositoryException("Unable to set a value with the invalid data name '" + name + "'. Only [a-zA-Z][a-zA-Z0-9-_]* are allowed."); 844 } 845 846 // Has value 847 if (hasValue(name, prefix)) 848 { 849 // Remove existing value to avoid values with same name (node + property) 850 removeValue(name, prefix); 851 } 852 } 853 854 public void removeValue(String name, String prefix) 855 { 856 try 857 { 858 _checkLock(); 859 String dataName = _getFullName(name, prefix); 860 861 if (_node.hasProperty(dataName)) 862 { 863 _node.getProperty(dataName).remove(); 864 } 865 else if (_node.hasNode(dataName)) 866 { 867 // Remove all existing nodes 868 NodeIterator it = _node.getNodes(dataName); 869 while (it.hasNext()) 870 { 871 Node next = it.nextNode(); 872 next.remove(); 873 } 874 } 875 else 876 { 877 throw new UnknownDataException("No data found for '" + dataName + "' in repository data at path '" + _getPathForLogs() + "'."); 878 } 879 } 880 catch (RepositoryException e) 881 { 882 throw new AmetysRepositoryException("Unable to remove data '" + _getFullName(name, prefix) + "' in repository data at path '" + _getPathForLogs() + "'.", e); 883 } 884 } 885 886 private String _getFullName(String name, String prefix) 887 { 888 return StringUtils.isEmpty(prefix) ? name : prefix + ":" + name; 889 } 890 891 public List<DataComment> getComments(String name) 892 { 893 List<DataComment> comments = new ArrayList<>(); 894 String commentsNodeName = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + name + RepositoryConstants.COMMENTS_SUFFIX; 895 896 try 897 { 898 if (_node.hasNode(commentsNodeName)) 899 { 900 Node commentsNode = _node.getNode(commentsNodeName); 901 NodeIterator iterator = commentsNode.getNodes(); 902 903 while (iterator.hasNext()) 904 { 905 Node node = iterator.nextNode(); 906 907 String text = node.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + "comment").getString(); 908 Date date = node.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + "date").getDate().getTime(); 909 String author = node.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + "author").getString(); 910 911 comments.add(new DataComment(text, date, author)); 912 } 913 } 914 915 return comments; 916 } 917 catch (RepositoryException e) 918 { 919 throw new AmetysRepositoryException("Unable to retrieve comments of data '" + name + "' in repository data at path '" + _getPathForLogs() + "'.", e); 920 } 921 } 922 923 public boolean hasComments(String name) 924 { 925 String commentsNodeName = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + name + RepositoryConstants.COMMENTS_SUFFIX; 926 927 try 928 { 929 if (_node.hasNode(commentsNodeName)) 930 { 931 Node commentsNode = _node.getNode(commentsNodeName); 932 return commentsNode.getNodes().hasNext(); 933 } 934 935 return false; 936 } 937 catch (RepositoryException e) 938 { 939 throw new AmetysRepositoryException("Unable to determine if the repository at path '" + _getPathForLogs() + "' has at least one comment for data '" + name + "'.", e); 940 } 941 } 942 943 public boolean hasComment(String name, int index) 944 { 945 String commentNodeName = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + name + RepositoryConstants.COMMENTS_SUFFIX + '/' + RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + index; 946 try 947 { 948 return _node.hasNode(commentNodeName); 949 } 950 catch (RepositoryException e) 951 { 952 throw new AmetysRepositoryException("Unable to determine if the repository at path '" + _getPathForLogs() + "' has a comment for data '" + name + "' at index '" + index + "'.", e); 953 } 954 } 955 956 public void addComment(String name, String text, String author, Date date) 957 { 958 if (text == null) 959 { 960 throw new NullPointerException("Unable to add a comment to data '" + name + "' in repository data at path '" + _getPathForLogs() + "'. Text of the comment cannot be null."); 961 } 962 963 String commentsNodeName = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + name + RepositoryConstants.COMMENTS_SUFFIX; 964 965 try 966 { 967 _checkLock(); 968 if (!_node.hasNode(commentsNodeName)) 969 { 970 _node.addNode(commentsNodeName, "ametys:compositeMetadata"); 971 } 972 973 Node commentsNode = _node.getNode(commentsNodeName); 974 String nodeName = null; 975 int i = 0; 976 977 // Loop over existing comments 978 do 979 { 980 nodeName = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + ++i; 981 } while (commentsNode.hasNode(nodeName)); 982 983 // Add the new comment node at the end 984 Node commentNode = commentsNode.addNode(nodeName, "ametys:compositeMetadata"); 985 986 GregorianCalendar calendar = new GregorianCalendar(); 987 if (date != null) 988 { 989 calendar.setTime(date); 990 } 991 992 commentNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + "comment", text); 993 commentNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + "date", calendar); 994 commentNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + "author", author); 995 } 996 catch (RepositoryException e) 997 { 998 throw new AmetysRepositoryException("Unable to add a comment to data '" + name + "' in repository data at path '" + _getPathForLogs() + "'.", e); 999 } 1000 } 1001 1002 public void editComment(String name, int index, String text, String author, Date date) 1003 { 1004 if (text == null) 1005 { 1006 throw new NullPointerException("Unable to edit the comment at index '" + index + "' of data '" + name + "' in repository data at path '" + _getPathForLogs() + "'. Text of the comment cannot be null."); 1007 } 1008 1009 String commentNodeName = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + name + RepositoryConstants.COMMENTS_SUFFIX + '/' + RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + index; 1010 1011 try 1012 { 1013 _checkLock(); 1014 Node commentNode = _node.getNode(commentNodeName); 1015 1016 GregorianCalendar calendar = new GregorianCalendar(); 1017 if (date != null) 1018 { 1019 calendar.setTime(date); 1020 } 1021 1022 commentNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + "comment", text); 1023 commentNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + "date", calendar); 1024 commentNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + "author", author); 1025 } 1026 catch (RepositoryException e) 1027 { 1028 throw new AmetysRepositoryException("Unable to edit the comment at index '" + index + "' of data '" + name + "' in repository data at path '" + _getPathForLogs() + "'.", e); 1029 } 1030 } 1031 1032 public void deleteComment(String name, int index) 1033 { 1034 String commentNodeName = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + name + RepositoryConstants.COMMENTS_SUFFIX + '/' + RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + index; 1035 1036 try 1037 { 1038 _checkLock(); 1039 _node.getNode(commentNodeName).remove(); 1040 } 1041 catch (RepositoryException e) 1042 { 1043 throw new AmetysRepositoryException("Unable to remove the comment at index '" + index + "' of data '" + name + "' in repository data at path '" + _getPathForLogs() + "'.", e); 1044 } 1045 } 1046 1047 private void _checkLock() throws RepositoryException 1048 { 1049 if (!_lockAlreadyChecked && _node.isLocked()) 1050 { 1051 LockManager lockManager = _node.getSession().getWorkspace().getLockManager(); 1052 1053 Lock lock = lockManager.getLock(_node.getPath()); 1054 Node lockHolder = lock.getNode(); 1055 1056 lockManager.addLockToken(lockHolder.getProperty(RepositoryConstants.METADATA_LOCKTOKEN).getString()); 1057 _lockAlreadyChecked = true; 1058 } 1059 } 1060 1061 /** 1062 * Retrieves the underlying node. 1063 * @return the underlying node. 1064 */ 1065 public Node getNode() 1066 { 1067 return _node; 1068 } 1069 1070 /** 1071 * Retrieves the current session 1072 * @return he session 1073 * @throws AmetysRepositoryException if an error occurs 1074 */ 1075 public Session getSession() throws AmetysRepositoryException 1076 { 1077 try 1078 { 1079 return _node.getSession(); 1080 } 1081 catch (RepositoryException e) 1082 { 1083 throw new AmetysRepositoryException("unable to retrieve the session of the repository data at path '" + _getPathForLogs() + "'.", e); 1084 } 1085 } 1086 1087 public String getDefaultPrefix() 1088 { 1089 return _defaultPrefix; 1090 } 1091 1092 public String getPath() 1093 { 1094 try 1095 { 1096 List<String> path = new ArrayList<>(); 1097 1098 if (!_node.isNodeType("ametys:object")) 1099 { 1100 RepositoryData parentData = new JCRRepositoryData(_node.getParent()); 1101 path.add(parentData.getPath()); 1102 } 1103 1104 path.add(_node.getName()); 1105 return StringUtils.join(path, "/"); 1106 } 1107 catch (RepositoryException e) 1108 { 1109 throw new AmetysRepositoryException("Unable to get the path of the repository data of node '" + _node + "'.", e); 1110 } 1111 } 1112 1113 private String _getPathForLogs() 1114 { 1115 try 1116 { 1117 return _node.getPath(); 1118 } 1119 catch (RepositoryException e) 1120 { 1121 throw new AmetysRepositoryException("Unable to get the path of the repository data of node '" + _node + "'.", e); 1122 } 1123 } 1124}