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