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