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.repository.jcr; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Date; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024 025import javax.jcr.Node; 026import javax.jcr.NodeIterator; 027import javax.jcr.Property; 028import javax.jcr.PropertyIterator; 029import javax.jcr.RepositoryException; 030import javax.jcr.version.OnParentVersionAction; 031import javax.jcr.version.Version; 032import javax.jcr.version.VersionException; 033import javax.jcr.version.VersionHistory; 034import javax.jcr.version.VersionIterator; 035 036import org.ametys.core.group.GroupIdentity; 037import org.ametys.core.user.UserIdentity; 038import org.ametys.plugins.repository.AmetysObject; 039import org.ametys.plugins.repository.AmetysRepositoryException; 040import org.ametys.plugins.repository.ModifiableACLAmetysObject; 041import org.ametys.plugins.repository.UnknownAmetysObjectException; 042import org.ametys.plugins.repository.data.holder.ModifiableModelLessDataHolder; 043import org.ametys.plugins.repository.data.holder.impl.DefaultModifiableModelLessDataHolder; 044import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 045import org.ametys.plugins.repository.data.repositorydata.impl.JCRRepositoryData; 046import org.ametys.plugins.repository.version.ModifiableDataAwareVersionableAmetysObject; 047import org.ametys.plugins.repository.version.VersionableAmetysObject; 048 049/** 050 * Default implementation of a {@link JCRAmetysObject}, which is also a {@link VersionableAmetysObject}. 051 * @param <F> the actual type of factory. 052 */ 053public class DefaultAmetysObject<F extends DefaultAmetysObjectFactory> extends SimpleAmetysObject<F> implements ModifiableDataAwareVersionableAmetysObject, ModifiableACLAmetysObject 054{ 055 056 /** Properties that are auto-created or protected, which mustn't be copied when copying a node. */ 057 protected static final List<String> PROTECTED_PROPERTIES = Arrays.asList("jcr:uuid", 058 "jcr:primaryType", "jcr:predecessors", "jcr:versionHistory", "jcr:baseVersion"); 059 060 // Root JCR Node of this content 061 private Node _baseNode; 062 063 // pointed version, or null if HEAD 064 private Version _versionNode; 065 066 // Current version, either HEAD or a given version 067 private Node _currentNode; 068 069 // The version history of the base node 070 private VersionHistory _versionHistory; 071 072 /** 073 * Creates an {@link DefaultAmetysObject}. 074 * @param node the node backing this {@link AmetysObject} 075 * @param parentPath the parentPath in the Ametys hierarchy 076 * @param factory the DefaultAmetysObjectFactory which created the AmetysObject 077 */ 078 public DefaultAmetysObject(Node node, String parentPath, F factory) 079 { 080 super(node, parentPath, factory); 081 _baseNode = node; 082 _currentNode = node; 083 } 084 085 @Override 086 public Node getNode() 087 { 088 return _currentNode; 089 } 090 091 /** 092 * Returns the JCR node backing this {@link AmetysObject} in the default JCR workspace 093 * @return the JCR node backing this {@link AmetysObject} in the default JCR workspace 094 */ 095 public Node getBaseNode () 096 { 097 return _baseNode; 098 } 099 100 // Versioning capabilities 101 102 /** 103 * Returns the JCR {@link VersionHistory} of the base node. 104 * @return the JCR {@link VersionHistory} of the base node. 105 * @throws RepositoryException if something wrong occurs retrieving the VersionHistory. 106 */ 107 protected VersionHistory getVersionHistory() throws RepositoryException 108 { 109 if (_versionHistory == null) 110 { 111 _versionHistory = _baseNode.getSession().getWorkspace().getVersionManager().getVersionHistory(_baseNode.getPath()); 112 } 113 114 return _versionHistory; 115 } 116 117 /** 118 * Returns the JCR base version of the node. 119 * @return the JCR base version of the node. 120 * @throws RepositoryException if something wrong occurs retrieving the base version. 121 */ 122 protected Version getBaseVersion() throws RepositoryException 123 { 124 return _baseNode.getSession().getWorkspace().getVersionManager().getBaseVersion(_baseNode.getPath()); 125 } 126 127 public void checkpoint() throws AmetysRepositoryException 128 { 129 try 130 { 131 getNode().getSession().getWorkspace().getVersionManager().checkpoint(getNode().getPath()); 132 } 133 catch (RepositoryException e) 134 { 135 throw new AmetysRepositoryException("Unable to checkpoint", e); 136 } 137 } 138 139 public void switchToLabel(String label) throws UnknownAmetysObjectException, AmetysRepositoryException 140 { 141 if (label == null) 142 { 143 // back to current version 144 _versionNode = null; 145 _currentNode = _baseNode; 146 } 147 else 148 { 149 try 150 { 151 VersionHistory history = getVersionHistory(); 152 _versionNode = history.getVersionByLabel(label); 153 _currentNode = _versionNode.getFrozenNode(); 154 } 155 catch (VersionException e) 156 { 157 throw new UnknownAmetysObjectException("There's no label : " + label, e); 158 } 159 catch (RepositoryException e) 160 { 161 throw new AmetysRepositoryException("Unable to switch to label : " + label, e); 162 } 163 } 164 } 165 166 public void switchToRevision(String revision) throws UnknownAmetysObjectException, AmetysRepositoryException 167 { 168 if (revision == null) 169 { 170 // back to current version 171 _versionNode = null; 172 _currentNode = _baseNode; 173 } 174 else 175 { 176 try 177 { 178 VersionHistory history = getVersionHistory(); 179 _versionNode = history.getVersion(revision); 180 _currentNode = _versionNode.getNode("jcr:frozenNode"); 181 } 182 catch (VersionException e) 183 { 184 throw new UnknownAmetysObjectException("There's no revision : " + revision, e); 185 } 186 catch (RepositoryException e) 187 { 188 throw new AmetysRepositoryException("Unable to switch to revision : " + revision, e); 189 } 190 } 191 } 192 193 public void restoreFromLabel(String label) throws UnknownAmetysObjectException, AmetysRepositoryException 194 { 195 try 196 { 197 VersionHistory history = getVersionHistory(); 198 Node versionNode = history.getVersionByLabel(label); 199 restoreFromNode(versionNode.getNode("jcr:frozenNode")); 200 } 201 catch (RepositoryException e) 202 { 203 throw new AmetysRepositoryException("Unable to restore from label: " + label, e); 204 } 205 } 206 207 public void restoreFromRevision(String revision) throws UnknownAmetysObjectException, AmetysRepositoryException 208 { 209 try 210 { 211 VersionHistory history = getVersionHistory(); 212 Node versionNode = history.getVersion(revision); 213 restoreFromNode(versionNode.getNode("jcr:frozenNode")); 214 } 215 catch (RepositoryException e) 216 { 217 throw new AmetysRepositoryException("Unable to restore from revision: " + revision, e); 218 } 219 } 220 221 /** 222 * Restore from a node 223 * @param node The node to restore 224 * @throws RepositoryException If error occurs 225 */ 226 protected void restoreFromNode(Node node) throws RepositoryException 227 { 228 // Remove all properties and nodes of the current node (except jcr and OnParentVersion=IGNORE). 229 PropertyIterator propIt = _baseNode.getProperties(); 230 while (propIt.hasNext()) 231 { 232 Property prop = propIt.nextProperty(); 233 String propName = prop.getName(); 234 if (!propName.startsWith("jcr:") && prop.getDefinition().getOnParentVersion() != OnParentVersionAction.IGNORE) 235 { 236 prop.remove(); 237 } 238 } 239 240 NodeIterator nodeIt = _baseNode.getNodes(); 241 while (nodeIt.hasNext()) 242 { 243 Node childNode = nodeIt.nextNode(); 244 String nodeName = childNode.getName(); 245 if (!nodeName.startsWith("jcr:") && childNode.getDefinition().getOnParentVersion() != OnParentVersionAction.IGNORE) 246 { 247 childNode.remove(); 248 } 249 } 250 251 // Copy all properties and nodes from the given node (except jcr). 252 PropertyIterator newPropIt = node.getProperties(); 253 while (newPropIt.hasNext()) 254 { 255 Property newProp = newPropIt.nextProperty(); 256 257 if (!newProp.getName().startsWith("jcr:")) 258 { 259 if (newProp.getDefinition().isMultiple()) 260 { 261 _baseNode.setProperty(newProp.getName(), newProp.getValues()); 262 } 263 else 264 { 265 _baseNode.setProperty(newProp.getName(), newProp.getValue()); 266 } 267 } 268 } 269 270 NodeIterator newNodeIt = node.getNodes(); 271 while (newNodeIt.hasNext()) 272 { 273 Node newNode = newNodeIt.nextNode(); 274 275 if (!newNode.getName().startsWith("jcr:")) 276 { 277 copyNode(_baseNode, newNode); 278 } 279 } 280 } 281 282 /** 283 * Copy the source node in parent node 284 * @param parentDest The dest node 285 * @param src The source node to copy 286 * @throws RepositoryException If error occurs 287 */ 288 protected void copyNode(Node parentDest, Node src) throws RepositoryException 289 { 290 Node dest; 291 if (parentDest.hasNode(src.getName())) 292 { 293 // case of auto created child 294 dest = parentDest.getNode(src.getName()); 295 } 296 else 297 { 298 dest = parentDest.addNode(src.getName(), src.getProperty("jcr:frozenPrimaryType").getString()); 299 } 300 301 PropertyIterator pit = src.getProperties(); 302 while (pit.hasNext()) 303 { 304 Property p = pit.nextProperty(); 305 String name = p.getName(); 306 307 // Tests for protected and/or autocreated properties 308 if (!PROTECTED_PROPERTIES.contains(name) && !name.startsWith("jcr:frozen") && !dest.hasProperty(name)) 309 { 310 if (p.getDefinition().isMultiple()) 311 { 312 dest.setProperty(name, p.getValues()); 313 } 314 else 315 { 316 dest.setProperty(name, p.getValue()); 317 } 318 } 319 } 320 321 NodeIterator nit = src.getNodes(); 322 while (nit.hasNext()) 323 { 324 copyNode(dest, nit.nextNode()); 325 } 326 } 327 328 public void addLabel(String label, boolean moveIfPresent) throws AmetysRepositoryException 329 { 330 try 331 { 332 VersionHistory history = getVersionHistory(); 333 String versionName; 334 335 if (_versionNode == null) 336 { 337 // not sticked to a particular version 338 versionName = getBaseVersion().getName(); 339 } 340 else 341 { 342 // sticked to label 343 versionName = _versionNode.getName(); 344 } 345 346 history.addVersionLabel(versionName, label, moveIfPresent); 347 } 348 catch (RepositoryException e) 349 { 350 throw new AmetysRepositoryException("Unable to add label : " + label, e); 351 } 352 } 353 354 public void removeLabel(String label) throws AmetysRepositoryException 355 { 356 try 357 { 358 VersionHistory history = getVersionHistory(); 359 history.removeVersionLabel(label); 360 } 361 catch (RepositoryException ex) 362 { 363 throw new AmetysRepositoryException("Unable to remove label : " + label, ex); 364 } 365 } 366 367 public String[] getAllLabels() throws AmetysRepositoryException 368 { 369 try 370 { 371 return getVersionHistory().getVersionLabels(); 372 } 373 catch (RepositoryException e) 374 { 375 throw new AmetysRepositoryException("Unable to retrieve list of labels", e); 376 } 377 } 378 379 public String[] getLabels() throws AmetysRepositoryException 380 { 381 try 382 { 383 Version version = _versionNode; 384 385 if (version == null) 386 { 387 // not sticked to a particular version 388 version = getBaseVersion(); 389 } 390 391 return version.getContainingHistory().getVersionLabels(version); 392 } 393 catch (RepositoryException e) 394 { 395 throw new AmetysRepositoryException("Unable to retrieve list of labels for current version", e); 396 } 397 } 398 399 400 public String[] getLabels(String revision) throws UnknownAmetysObjectException, AmetysRepositoryException 401 { 402 try 403 { 404 VersionHistory history = getVersionHistory(); 405 Version version = history.getVersion(revision); 406 return history.getVersionLabels(version); 407 } 408 catch (VersionException e) 409 { 410 throw new UnknownAmetysObjectException("There's no revision " + revision, e); 411 } 412 catch (RepositoryException e) 413 { 414 throw new AmetysRepositoryException("Unable to retrieve list of labels for current version", e); 415 } 416 } 417 418 public String getRevision() throws AmetysRepositoryException 419 { 420 if (_versionNode == null) 421 { 422 // Current version 423 return null; 424 } 425 426 try 427 { 428 return _versionNode.getName(); 429 } 430 catch (RepositoryException e) 431 { 432 throw new AmetysRepositoryException("Unable to get revision", e); 433 } 434 } 435 436 @Override 437 public Date getRevisionTimestamp() throws AmetysRepositoryException 438 { 439 if (_versionNode == null) 440 { 441 // Current version 442 return null; 443 } 444 445 try 446 { 447 return _versionNode.getCreated().getTime(); 448 } 449 catch (RepositoryException e) 450 { 451 throw new AmetysRepositoryException("Unable to get revision date", e); 452 } 453 } 454 455 @Override 456 public Date getRevisionTimestamp(String revision) throws UnknownAmetysObjectException, AmetysRepositoryException 457 { 458 try 459 { 460 VersionHistory history = getVersionHistory(); 461 Version version = history.getVersion(revision); 462 463 return version.getCreated().getTime(); 464 } 465 catch (VersionException e) 466 { 467 throw new UnknownAmetysObjectException("There's no revision " + revision, e); 468 } 469 catch (RepositoryException e) 470 { 471 throw new AmetysRepositoryException("Unable to get revision date", e); 472 } 473 } 474 475 public String[] getAllRevisions() throws AmetysRepositoryException 476 { 477 try 478 { 479 List<String> revisions = new ArrayList<>(); 480 VersionIterator iterator = getVersionHistory().getAllVersions(); 481 482 while (iterator.hasNext()) 483 { 484 String name = iterator.nextVersion().getName(); 485 if (!"jcr:rootVersion".equals(name)) 486 { 487 revisions.add(name); 488 } 489 } 490 491 return revisions.toArray(new String[revisions.size()]); 492 } 493 catch (RepositoryException ex) 494 { 495 throw new AmetysRepositoryException("Unable to get revisions list", ex); 496 } 497 } 498 499 @Override 500 public ModifiableModelLessDataHolder getUnversionedDataHolder() 501 { 502 try 503 { 504 ModifiableRepositoryData repositoryData = new JCRRepositoryData(_baseNode.getNode("ametys-internal:unversioned")); 505 return new DefaultModifiableModelLessDataHolder(_getFactory().getUnversionedDataTypeExtensionPoint(), repositoryData); 506 } 507 catch (RepositoryException e) 508 { 509 throw new AmetysRepositoryException(e); 510 } 511 } 512 513 @Override 514 public Set<String> getAllowedProfilesForAnyConnectedUser() 515 { 516 return ACLJCRAmetysObjectHelper.getAllowedProfilesForAnyConnectedUser(getNode()); 517 } 518 519 @Override 520 public void addAllowedProfilesForAnyConnectedUser(Set<String> profileIds) 521 { 522 ACLJCRAmetysObjectHelper.addAllowedProfilesForAnyConnectedUser(getNode(), profileIds); 523 } 524 525 @Override 526 public void removeAllowedProfilesForAnyConnectedUser(Set<String> profileIds) 527 { 528 ACLJCRAmetysObjectHelper.removeAllowedProfilesForAnyConnectedUser(getNode(), profileIds); 529 } 530 531 @Override 532 public Set<String> getDeniedProfilesForAnyConnectedUser() 533 { 534 return ACLJCRAmetysObjectHelper.getDeniedProfilesForAnyConnectedUser(getNode()); 535 } 536 537 @Override 538 public void addDeniedProfilesForAnyConnectedUser(Set<String> profileIds) 539 { 540 ACLJCRAmetysObjectHelper.addDeniedProfilesForAnyConnectedUser(getNode(), profileIds); 541 } 542 543 @Override 544 public void removeDeniedProfilesForAnyConnectedUser(Set<String> profileIds) 545 { 546 ACLJCRAmetysObjectHelper.removeDeniedProfilesForAnyConnectedUser(getNode(), profileIds); 547 } 548 549 @Override 550 public Set<String> getAllowedProfilesForAnonymous() 551 { 552 return ACLJCRAmetysObjectHelper.getAllowedProfilesForAnonymous(getNode()); 553 } 554 555 @Override 556 public void addAllowedProfilesForAnonymous(Set<String> profileIds) 557 { 558 ACLJCRAmetysObjectHelper.addAllowedProfilesForAnonymous(getNode(), profileIds); 559 } 560 561 @Override 562 public void removeAllowedProfilesForAnonymous(Set<String> profileIds) 563 { 564 ACLJCRAmetysObjectHelper.removeAllowedProfilesForAnonymous(getNode(), profileIds); 565 } 566 567 @Override 568 public Set<String> getDeniedProfilesForAnonymous() 569 { 570 return ACLJCRAmetysObjectHelper.getDeniedProfilesForAnonymous(getNode()); 571 } 572 573 @Override 574 public void addDeniedProfilesForAnonymous(Set<String> profileIds) 575 { 576 ACLJCRAmetysObjectHelper.addDeniedProfilesForAnonymous(getNode(), profileIds); 577 } 578 579 @Override 580 public void removeDeniedProfilesForAnonymous(Set<String> profileIds) 581 { 582 ACLJCRAmetysObjectHelper.removeDeniedProfilesForAnonymous(getNode(), profileIds); 583 } 584 585 @Override 586 public Set<String> getAllowedProfilesForUser(UserIdentity user) 587 { 588 return ACLJCRAmetysObjectHelper.getAllowedProfilesForUser(getNode(), user); 589 } 590 591 @Override 592 public Map<UserIdentity, Set<String>> getAllowedProfilesForUsers() 593 { 594 return ACLJCRAmetysObjectHelper.getAllowedProfilesForUsers(getNode()); 595 } 596 597 @Override 598 public Set<UserIdentity> getAllowedUsers(String profileId) 599 { 600 return ACLJCRAmetysObjectHelper.getAllowedUsers(getNode(), profileId); 601 } 602 603 @Override 604 public void addAllowedUsers(Set<UserIdentity> users, String profileId) 605 { 606 ACLJCRAmetysObjectHelper.addAllowedUsers(users, getNode(), profileId); 607 } 608 609 @Override 610 public void removeAllowedUsers(Set<UserIdentity> users, String profileId) 611 { 612 ACLJCRAmetysObjectHelper.removeAllowedUsers(users, getNode(), profileId); 613 } 614 615 @Override 616 public void removeAllowedUsers(Set<UserIdentity> users) 617 { 618 ACLJCRAmetysObjectHelper.removeAllowedUsers(users, getNode()); 619 } 620 621 @Override 622 public Map<GroupIdentity, Set<String>> getAllowedProfilesForGroups() 623 { 624 return ACLJCRAmetysObjectHelper.getAllowedProfilesForGroups(getNode()); 625 } 626 627 @Override 628 public Set<GroupIdentity> getAllowedGroups(String profileId) 629 { 630 return ACLJCRAmetysObjectHelper.getAllowedGroups(getNode(), profileId); 631 } 632 633 @Override 634 public void addAllowedGroups(Set<GroupIdentity> groups, String profileId) 635 { 636 ACLJCRAmetysObjectHelper.addAllowedGroups(groups, getNode(), profileId); 637 } 638 639 @Override 640 public void removeAllowedGroups(Set<GroupIdentity> groups, String profileId) 641 { 642 ACLJCRAmetysObjectHelper.removeAllowedGroups(groups, getNode(), profileId); 643 } 644 645 @Override 646 public void removeAllowedGroups(Set<GroupIdentity> groups) 647 { 648 ACLJCRAmetysObjectHelper.removeAllowedGroups(groups, getNode()); 649 } 650 651 @Override 652 public Set<String> getDeniedProfilesForUser(UserIdentity user) 653 { 654 return ACLJCRAmetysObjectHelper.getDeniedProfilesForUser(getNode(), user); 655 } 656 657 @Override 658 public Map<UserIdentity, Set<String>> getDeniedProfilesForUsers() 659 { 660 return ACLJCRAmetysObjectHelper.getDeniedProfilesForUsers(getNode()); 661 } 662 663 @Override 664 public Set<UserIdentity> getDeniedUsers(String profileId) 665 { 666 return ACLJCRAmetysObjectHelper.getDeniedUsers(getNode(), profileId); 667 } 668 669 @Override 670 public void addDeniedUsers(Set<UserIdentity> users, String profileId) 671 { 672 ACLJCRAmetysObjectHelper.addDeniedUsers(users, getNode(), profileId); 673 } 674 675 @Override 676 public void removeDeniedUsers(Set<UserIdentity> users, String profileId) 677 { 678 ACLJCRAmetysObjectHelper.removeDeniedUsers(users, getNode(), profileId); 679 } 680 681 @Override 682 public void removeDeniedUsers(Set<UserIdentity> users) 683 { 684 ACLJCRAmetysObjectHelper.removeDeniedUsers(users, getNode()); 685 } 686 687 @Override 688 public Map<GroupIdentity, Set<String>> getDeniedProfilesForGroups() 689 { 690 return ACLJCRAmetysObjectHelper.getDeniedProfilesForGroups(getNode()); 691 } 692 693 @Override 694 public Set<GroupIdentity> getDeniedGroups(String profileId) 695 { 696 return ACLJCRAmetysObjectHelper.getDeniedGroups(getNode(), profileId); 697 } 698 699 @Override 700 public void addDeniedGroups(Set<GroupIdentity> groups, String profileId) 701 { 702 ACLJCRAmetysObjectHelper.addDeniedGroups(groups, getNode(), profileId); 703 } 704 705 @Override 706 public void removeDeniedGroups(Set<GroupIdentity> groups, String profileId) 707 { 708 ACLJCRAmetysObjectHelper.removeDeniedGroups(groups, getNode(), profileId); 709 } 710 711 @Override 712 public void removeDeniedGroups(Set<GroupIdentity> groups) 713 { 714 ACLJCRAmetysObjectHelper.removeDeniedGroups(groups, getNode()); 715 } 716 717 @Override 718 public boolean isInheritanceDisallowed() 719 { 720 return ACLJCRAmetysObjectHelper.isInheritanceDisallowed(getNode()); 721 } 722 723 @Override 724 public void disallowInheritance(boolean disallow) 725 { 726 ACLJCRAmetysObjectHelper.disallowInheritance(getNode(), disallow); 727 } 728}