/* * Copyright (C) 2005-2008 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's * FLOSS exception. You should have recieved a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.repo.version; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.policy.PolicyScope; import org.alfresco.repo.version.common.VersionHistoryImpl; import org.alfresco.repo.version.common.VersionImpl; import org.alfresco.repo.version.common.VersionUtil; import org.alfresco.service.cmr.repository.AspectMissingException; import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.version.ReservedVersionNameException; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionHistory; import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.cmr.version.VersionServiceException; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.ParameterCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Version2 Service - implements version2Store (a lighter implementation of the lightWeightVersionStore) */ public class Version2ServiceImpl extends VersionServiceImpl implements VersionService, Version2Model { private static Log logger = LogFactory.getLog(Version2ServiceImpl.class); protected boolean useDeprecatedV1 = false; // bypass V2, only use V1 private PermissionService permissionService; public void setPermissionService(PermissionService permissionService) { this.permissionService = permissionService; } public void setOnlyUseDeprecatedV1(boolean useDeprecatedV1) { this.useDeprecatedV1 = useDeprecatedV1; } /** * Initialise method */ @Override public void initialise() { super.initialise(); if (useDeprecatedV1) { logger.warn("version.store.onlyUseDeprecatedV1=true - using deprecated 'lightWeightVersionStore' by default (not 'version2Store')"); } } /* (non-Javadoc) * @see org.alfresco.repo.service.cmr.version.VersionService#getVersionStoreReference() */ @Override public StoreRef getVersionStoreReference() { if (useDeprecatedV1) { return super.getVersionStoreReference(); } return new StoreRef(StoreRef.PROTOCOL_WORKSPACE, Version2Model.STORE_ID); } /* (non-Javadoc) * @see org.alfresco.repo.service.cmr.version.VersionService#createVersion(org.alfresco.service.cmr.repository.NodeRef, java.util.Map) */ public Version createVersion( NodeRef nodeRef, Map versionProperties) throws ReservedVersionNameException, AspectMissingException { if (useDeprecatedV1) { return super.createVersion(nodeRef, versionProperties); } long startTime = System.currentTimeMillis(); // Get the next version number int versionNumber = this.versionCounterService.nextVersionNumber(getVersionStoreReference()); // Create the version Version version = createVersion(nodeRef, versionProperties, versionNumber); if (logger.isDebugEnabled()) { logger.debug("created version (" + versionNumber + " - " + VersionUtil.convertNodeRef(version.getFrozenStateNodeRef()) + ") in " + (System.currentTimeMillis()-startTime) + " ms"); } return version; } /* (non-Javadoc) * @see org.alfresco.repo.service.cmr.version.VersionService#createVersion(java.util.Collection, java.util.Map) */ public Collection createVersion( Collection nodeRefs, Map versionProperties) throws ReservedVersionNameException, AspectMissingException { /* * Note: we can't control the order of the list, so if we have children and parents in the list and the * parents get versioned before the children and the children are not already versioned then the parents * child references will be pointing to the node ref, rather than the version history. */ if (useDeprecatedV1) { return super.createVersion(nodeRefs, versionProperties); } long startTime = System.currentTimeMillis(); Collection result = new ArrayList(nodeRefs.size()); // Get the next version number int versionNumber = this.versionCounterService.nextVersionNumber(getVersionStoreReference()); // Version each node in the list for (NodeRef nodeRef : nodeRefs) { result.add(createVersion(nodeRef, versionProperties, versionNumber)); } if (logger.isDebugEnabled()) { logger.debug("created version list (" + versionNumber + " - " + getVersionStoreReference() + ") in "+ (System.currentTimeMillis()-startTime) +" ms (with " + nodeRefs.size() + " nodes)"); } return result; } protected Version createVersion( NodeRef nodeRef, Map origVersionProperties, int versionNumber) throws ReservedVersionNameException { if (useDeprecatedV1) { return super.createVersion(nodeRef, origVersionProperties, versionNumber); } long startTime = System.currentTimeMillis(); // Copy the version properties (to prevent unexpected side effects to the caller) Map versionProperties = new HashMap(); if (origVersionProperties != null) { versionProperties.putAll(origVersionProperties); } // If the version aspect is not there then add it to the 'live' (versioned) node if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == false) { this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, null); } // Call the policy behaviour invokeBeforeCreateVersion(nodeRef); // version "description" property is added as a standard version property (if not null) rather than a metadata version property String versionDescription = (String)versionProperties.get(Version.PROP_DESCRIPTION); versionProperties.remove(Version.PROP_DESCRIPTION); // don'tĀ freeze previous version label versionProperties.remove(ContentModel.PROP_VERSION_LABEL); // Check that the supplied additional version properties do not clash with the reserved ones VersionUtil.checkVersionPropertyNames(versionProperties.keySet()); // Check the repository for the version history for this node NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef); NodeRef currentVersionRef = null; if (versionHistoryRef == null) { // Create the version history versionHistoryRef = createVersionHistory(nodeRef); } else { // Since we have an existing version history we should be able to lookup // the current version currentVersionRef = getCurrentVersionNodeRef(versionHistoryRef, nodeRef); if (currentVersionRef == null) { throw new VersionServiceException(MSGID_ERR_NOT_FOUND); } // Need to check that we are not about to create branch since this is not currently supported VersionHistory versionHistory = buildVersionHistory(versionHistoryRef, nodeRef); Version currentVersion = getVersion(currentVersionRef); if (versionHistory.getSuccessors(currentVersion).size() != 0) { throw new VersionServiceException(MSGID_ERR_NO_BRANCHES); } } // Create the node details QName classRef = this.nodeService.getType(nodeRef); PolicyScope nodeDetails = new PolicyScope(classRef); // Get the node details by calling the onVersionCreate policy behaviour invokeOnCreateVersion(nodeRef, versionProperties, nodeDetails); // Calculate the version label Version preceedingVersion = getVersion(currentVersionRef); String versionLabel = invokeCalculateVersionLabel(classRef, preceedingVersion, versionNumber, versionProperties); // Extract Type Definition QName sourceTypeRef = nodeService.getType(nodeRef); long nodeDbId = (Long)this.nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_DBID); Set nodeAspects = this.nodeService.getAspects(nodeRef); // Create the new version node (child of the version history) NodeRef newVersionRef = createNewVersion( sourceTypeRef, versionHistoryRef, getStandardVersionProperties(nodeRef, nodeDbId, nodeAspects, versionNumber, versionLabel, versionDescription), versionProperties, versionNumber, nodeDetails); if (currentVersionRef == null) { // Set the new version to be the root version in the version history this.dbNodeService.createAssociation( versionHistoryRef, newVersionRef, Version2Model.ASSOC_ROOT_VERSION); } // Create the version data object Version version = getVersion(newVersionRef); // Set the new version label on the 'live' (versioned) node this.nodeService.setProperty( nodeRef, ContentModel.PROP_VERSION_LABEL, version.getVersionLabel()); // Invoke the policy behaviour invokeAfterCreateVersion(nodeRef, version); if (logger.isTraceEnabled()) { logger.trace("created version (" + versionNumber + " - " + getVersionStoreReference() + ") " + newVersionRef + " " + (System.currentTimeMillis()-startTime) +" ms"); } // Return the data object representing the newly created version return version; } /** * Creates a new version history node, applying the root version aspect is required * * @param nodeRef the node ref * @return the version history node reference */ protected NodeRef createVersionHistory(NodeRef nodeRef) { HashMap props = new HashMap(); props.put(ContentModel.PROP_NAME, nodeRef.getId()); props.put(Version2Model.PROP_QNAME_VERSIONED_NODE_ID, nodeRef.getId()); // Create a new version history node ChildAssociationRef childAssocRef = this.dbNodeService.createNode( getRootNode(), Version2Model.CHILD_QNAME_VERSION_HISTORIES, QName.createQName(Version2Model.NAMESPACE_URI, nodeRef.getId()), Version2Model.TYPE_QNAME_VERSION_HISTORY, props); if (logger.isTraceEnabled()) { logger.trace("created version history nodeRef: " + childAssocRef.getChildRef() + " for " + nodeRef); } return childAssocRef.getChildRef(); } /* (non-Javadoc) * @see org.alfresco.repo.service.cmr.version.VersionService#getVersionHistory(org.alfresco.service.cmr.repository.NodeRef) */ public VersionHistory getVersionHistory(NodeRef nodeRef) { if (useDeprecatedV1) { return super.getVersionHistory(nodeRef); } VersionHistory versionHistory = null; // get version history, if the 'live' (versioned) node exists if (this.nodeService.exists(nodeRef) == true) { NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef); if (versionHistoryRef != null) { versionHistory = buildVersionHistory(versionHistoryRef, nodeRef); } } return versionHistory; } /* (non-Javadoc) * @see org.alfresco.repo.service.cmr.version.VersionService#getCurrentVersion(org.alfresco.service.cmr.repository.NodeRef) */ public Version getCurrentVersion(NodeRef nodeRef) { if (useDeprecatedV1) { return super.getCurrentVersion(nodeRef); } Version version = null; // get the current version, if the 'live' (versioned) node has the "versionable" aspect if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true) { VersionHistory versionHistory = getVersionHistory(nodeRef); if (versionHistory != null) { String versionLabel = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL); version = versionHistory.getVersion(versionLabel); } } return version; } /** * Get a map containing the standard list of version properties populated. * * @param nodeRef the node reference * @return the standard version properties */ protected Map getStandardVersionProperties(NodeRef nodeRef, long nodeDbId, Set nodeAspects, int versionNumber, String versionLabel, String versionDescription) { Map result = new HashMap(10); // Set the version number result.put(Version2Model.PROP_QNAME_VERSION_NUMBER, Integer.toString(versionNumber)); // Set the version label result.put(Version2Model.PROP_QNAME_VERSION_LABEL, versionLabel); // Set the version description (can be null) result.put(Version2Model.PROP_QNAME_VERSION_DESCRIPTION, versionDescription); // Set the versionable node store id result.put(Version2Model.PROP_QNAME_FROZEN_NODE_REF, nodeRef); // Set the versionable node store id result.put(Version2Model.PROP_QNAME_FROZEN_NODE_DBID, nodeDbId); return result; } /** * Creates a new version node, setting the properties both calculated and specified. * * @param versionableNodeRef the reference to the node being versioned * @param versionHistoryRef version history node reference * @param preceedingNodeRef the version node preceeding this in the version history * , null if none * @param versionProperties version properties * @param versionNumber the version number * @return the version node reference */ protected NodeRef createNewVersion( QName sourceTypeRef, NodeRef versionHistoryRef, Map standardVersionProperties, Map versionProperties, int versionNumber, PolicyScope nodeDetails) { ChildAssociationRef childAssocRef = null; // Disable auto-version behaviour this.policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_VERSIONABLE); this.policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); this.policyBehaviourFilter.disableBehaviour(ContentModel.TYPE_MULTILINGUAL_CONTAINER); NodeRef versionNodeRef = null; try { // "copy" type and properties childAssocRef = this.dbNodeService.createNode( versionHistoryRef, Version2Model.CHILD_QNAME_VERSIONS, QName.createQName(Version2Model.NAMESPACE_URI, Version2Model.CHILD_VERSIONS+"-"+versionNumber), // TODO - testing - note: all children (of a versioned node) will have the same version number, maybe replace with a version sequence of some sort 001-...00n sourceTypeRef, nodeDetails.getProperties()); versionNodeRef = childAssocRef.getChildRef(); // NOTE: special ML case - see also MultilingualContentServiceImpl.makeMLContainer if (sourceTypeRef.equals(ContentModel.TYPE_MULTILINGUAL_CONTAINER)) { // Set the permissions to allow anything by anyone permissionService.setPermission( versionNodeRef, PermissionService.ALL_AUTHORITIES, PermissionService.ALL_PERMISSIONS, true); permissionService.setPermission( versionNodeRef, PermissionService.GUEST_AUTHORITY, PermissionService.ALL_PERMISSIONS, true); } // add aspect with the standard version properties to the 'version' node nodeService.addAspect(versionNodeRef, Version2Model.ASPECT_VERSION, standardVersionProperties); // store the meta data storeVersionMetaData(versionNodeRef, versionProperties); freezeChildAssociations(versionNodeRef, nodeDetails.getChildAssociations()); freezeAspects(nodeDetails, versionNodeRef, nodeDetails.getAspects()); } finally { // Enable auto-version behaviour this.policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_VERSIONABLE); this.policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); this.policyBehaviourFilter.enableBehaviour(ContentModel.TYPE_MULTILINGUAL_CONTAINER); } // If the auditable aspect is not there then add it to the 'version' node (after original aspects have been frozen) if (dbNodeService.hasAspect(versionNodeRef, ContentModel.ASPECT_AUDITABLE) == false) { dbNodeService.addAspect(versionNodeRef, ContentModel.ASPECT_AUDITABLE, null); } if (logger.isTraceEnabled()) { logger.trace("newVersion created (" + versionNumber + ") " + versionNodeRef); } // Return the created node reference return versionNodeRef; } /** * Store the version meta data * * @param versionNodeRef the version node reference * @param versionProperties the version properties */ private void storeVersionMetaData(NodeRef versionNodeRef, Map versionProperties) { // TODO - these are stored as "residual" properties (ie. without property type) - see also NodeBrowser // - need to review, eg. how can we store arbitrary map of metadata properties, that could be indexed/searched (if configured against versionStore) for (Map.Entry entry : versionProperties.entrySet()) { dbNodeService.setProperty(versionNodeRef, QName.createQName(Version2Model.NAMESPACE_URI, Version2Model.PROP_METADATA_PREFIX+entry.getKey()), entry.getValue()); } } /** * Freeze the aspects * * @param nodeDetails the node details * @param versionNodeRef the version node reference * @param aspects the set of aspects */ private void freezeAspects(PolicyScope nodeDetails, NodeRef versionNodeRef, Set aspects) { for (QName aspect : aspects) { if (logger.isTraceEnabled()) { logger.trace("freezeAspect: " + versionNodeRef + " " + aspect); } if (aspect.equals(ContentModel.ASPECT_AUDITABLE)) { // freeze auditable aspect properties (eg. created, creator, modifed, modifier, accessed) for (Map.Entry entry : nodeDetails.getProperties(aspect).entrySet()) { if (entry.getKey().equals(ContentModel.PROP_CREATOR)) { dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_CREATOR, entry.getValue()); } else if (entry.getKey().equals(ContentModel.PROP_CREATED)) { dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_CREATED, entry.getValue()); } else if (entry.getKey().equals(ContentModel.PROP_MODIFIER)) { dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_MODIFIER, entry.getValue()); } else if (entry.getKey().equals(ContentModel.PROP_MODIFIED)) { dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_MODIFIED, entry.getValue()); } else if (entry.getKey().equals(ContentModel.PROP_ACCESSED)) { dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_ACCESSED, entry.getValue()); } else { throw new AlfrescoRuntimeException("Unexpected auditable property: " + entry.getKey()); } } } else { // Freeze the details of the aspect dbNodeService.addAspect(versionNodeRef, aspect, nodeDetails.getProperties(aspect)); } } } private void freezeChildAssociations(NodeRef versionNodeRef, List childAssociations) { for (ChildAssociationRef childAssocRef : childAssociations) { HashMap properties = new HashMap(); QName sourceTypeRef = nodeService.getType(childAssocRef.getChildRef()); // Set the reference property to point to the child node properties.put(ContentModel.PROP_REFERENCE, childAssocRef.getChildRef()); // Create child version reference this.dbNodeService.createNode( versionNodeRef, childAssocRef.getTypeQName(), childAssocRef.getQName(), sourceTypeRef, properties); } } /** * Builds a version history object from the version history reference. *

* The node ref is passed to enable the version history to be scoped to the * appropriate branch in the version history. * * @param versionHistoryRef the node ref for the version history * @param nodeRef the node reference * @return a constructed version history object */ protected VersionHistory buildVersionHistory(NodeRef versionHistoryRef, NodeRef nodeRef) { if (useDeprecatedV1) { return super.buildVersionHistory(versionHistoryRef, nodeRef); } VersionHistory versionHistory = null; List versionsAssoc = this.dbNodeService.getChildAssocs(versionHistoryRef, Version2Model.CHILD_QNAME_VERSIONS, RegexQNamePattern.MATCH_ALL); Map versionHistoryMap = new HashMap(versionsAssoc.size()); for (ChildAssociationRef versionAssoc : versionsAssoc) { String localName = versionAssoc.getQName().getLocalName(); if (localName.indexOf(Version2Model.CHILD_VERSIONS+"-") != -1) // TODO - could remove this belts-and-braces, should match correctly above ! { int versionNumber = Integer.parseInt(localName.substring((Version2Model.CHILD_VERSIONS+"-").length())); versionHistoryMap.put(versionNumber, versionAssoc.getChildRef()); } } Map sortedMap = new TreeMap(versionHistoryMap); // Build the version history object boolean isRoot = true; Version preceeding = null; for (NodeRef versionRef : sortedMap.values()) { Version version = getVersion(versionRef); if (isRoot == true) { versionHistory = new VersionHistoryImpl(version); isRoot = false; } else { ((VersionHistoryImpl)versionHistory).addVersion(version, preceeding); } preceeding = version; } return versionHistory; } /** * Constructs the a version object to contain the version information from the version node ref. * * @param versionRef the version reference * @return object containing verison data */ protected Version getVersion(NodeRef versionRef) { if (useDeprecatedV1) { return super.getVersion(versionRef); } if (versionRef == null) { return null; } Map versionProperties = new HashMap(); // Get the standard node details and get the meta data Map nodeProperties = this.dbNodeService.getProperties(versionRef); if (logger.isTraceEnabled()) { logger.trace("getVersion: " + versionRef + " " + nodeProperties.keySet()); } // TODO consolidate with VersionUtil.convertFrozenToOriginalProps for (QName key : nodeProperties.keySet()) { Serializable value = nodeProperties.get(key); String keyName = key.getLocalName(); int idx = keyName.indexOf(Version2Model.PROP_METADATA_PREFIX); if (idx == 0) { // versioned metadata property - additional (optional) metadata, set during versioning versionProperties.put(keyName.substring(Version2Model.PROP_METADATA_PREFIX.length()), value); } else { if (key.equals(Version2Model.PROP_QNAME_VERSION_DESCRIPTION)) { versionProperties.put(Version.PROP_DESCRIPTION, (String)value); } else if (key.equals(Version2Model.PROP_QNAME_VERSION_LABEL)) { versionProperties.put(VersionBaseModel.PROP_VERSION_LABEL, (String)value); } else if (key.equals(Version2Model.PROP_QNAME_VERSION_NUMBER)) { versionProperties.put(VersionBaseModel.PROP_VERSION_NUMBER, (String)value); } else { // all other properties versionProperties.put(keyName, value); } } } // Create and return the version object NodeRef newNodeRef = new NodeRef(new StoreRef(Version2Model.STORE_PROTOCOL, Version2Model.STORE_ID), versionRef.getId()); Version result = new VersionImpl(versionProperties, newNodeRef); // done return result; } /** * Gets a reference to the version history node for a given 'real' node. * * @param nodeRef a node reference * @return a reference to the version history node, null of none */ protected NodeRef getVersionHistoryNodeRef(NodeRef nodeRef) { if (useDeprecatedV1) { return super.getVersionHistoryNodeRef(nodeRef); } return this.dbNodeService.getChildByName(getRootNode(), Version2Model.CHILD_QNAME_VERSION_HISTORIES, nodeRef.getId()); } /** * Gets a reference to the node for the current version of the passed node ref. * * This uses the version label as a mechanism for looking up the version node in * the version history. * * @param nodeRef a node reference * @return a reference to a version reference */ private NodeRef getCurrentVersionNodeRef(NodeRef versionHistory, NodeRef nodeRef) { NodeRef result = null; String versionLabel = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL); Collection versions = this.dbNodeService.getChildAssocs(versionHistory); for (ChildAssociationRef version : versions) { String tempLabel = (String)this.dbNodeService.getProperty(version.getChildRef(), Version2Model.PROP_QNAME_VERSION_LABEL); if (tempLabel != null && tempLabel.equals(versionLabel) == true) { result = version.getChildRef(); break; } } return result; } /** * @see org.alfresco.cms.version.VersionService#revert(NodeRef) */ public void revert(NodeRef nodeRef) { if (useDeprecatedV1) { super.revert(nodeRef, getCurrentVersion(nodeRef), true); } else { revert(nodeRef, getCurrentVersion(nodeRef), true); } } /** * @see org.alfresco.service.cmr.version.VersionService#revert(org.alfresco.service.cmr.repository.NodeRef, boolean) */ public void revert(NodeRef nodeRef, boolean deep) { if (useDeprecatedV1) { super.revert(nodeRef, getCurrentVersion(nodeRef), deep); } else { revert(nodeRef, getCurrentVersion(nodeRef), deep); } } /** * @see org.alfresco.service.cmr.version.VersionService#revert(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.version.Version) */ public void revert(NodeRef nodeRef, Version version) { if (useDeprecatedV1) { super.revert(nodeRef, version, true); } else { revert(nodeRef, version, true); } } /** * @see org.alfresco.service.cmr.version.VersionService#revert(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.version.Version, boolean) */ public void revert(NodeRef nodeRef, Version version, boolean deep) { if (useDeprecatedV1) { super.revert(nodeRef, version, deep); } else { // Check the mandatory parameters ParameterCheck.mandatory("nodeRef", nodeRef); ParameterCheck.mandatory("version", version); // Cross check that the version provided relates to the node reference provided if (nodeRef.getId().equals(((NodeRef)version.getVersionProperty(Version2Model.PROP_FROZEN_NODE_REF)).getId()) == false) { // Error since the version provided does not correspond to the node reference provided throw new VersionServiceException(MSGID_ERR_REVERT_MISMATCH); } // Turn off any auto-version policy behaviours this.policyBehaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); try { // Store the current version label String currentVersionLabel = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL); // Get the node that represents the frozen state NodeRef versionNodeRef = version.getFrozenStateNodeRef(); // Revert the property values Map props = this.nodeService.getProperties(versionNodeRef); VersionUtil.convertFrozenToOriginalProps(props); this.nodeService.setProperties(nodeRef, props); // Apply/remove the aspects as required Set aspects = new HashSet(this.nodeService.getAspects(nodeRef)); for (QName versionAspect : this.nodeService.getAspects(versionNodeRef)) { if (aspects.contains(versionAspect) == false) { this.nodeService.addAspect(nodeRef, versionAspect, null); } else { aspects.remove(versionAspect); } } for (QName aspect : aspects) { this.nodeService.removeAspect(nodeRef, aspect); } // Re-add the versionable aspect to the reverted node if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == false) { this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, null); } // Re-set the version label property (since it should not be modified from the original) this.nodeService.setProperty(nodeRef, ContentModel.PROP_VERSION_LABEL, currentVersionLabel); // Add/remove the child nodes List children = new ArrayList(this.nodeService.getChildAssocs(nodeRef)); for (ChildAssociationRef versionedChild : this.nodeService.getChildAssocs(versionNodeRef)) { if (children.contains(versionedChild) == false) { if (this.nodeService.exists(versionedChild.getChildRef()) == true) { // The node was a primary child of the parent, but that is no longer the case. Dispite this // the node still exits so this means it has been moved. // The best thing to do in this situation will be to re-add the node as a child, but it will not // be a primary child. this.nodeService.addChild(nodeRef, versionedChild.getChildRef(), versionedChild.getTypeQName(), versionedChild.getQName()); } else { if (versionedChild.isPrimary() == true) { // Only try to resotre missing children if we are doing a deep revert // Look and see if we have a version history for the child node if (deep == true && getVersionHistoryNodeRef(versionedChild.getChildRef()) != null) { // We're going to try and restore the missing child node and recreate the assoc restore( versionedChild.getChildRef(), nodeRef, versionedChild.getTypeQName(), versionedChild.getQName()); } // else the deleted child did not have a version history so we can't restore the child // and so we can't revert the association } // else // Since this was never a primary assoc and the child has been deleted we won't recreate // the missing node as it was never owned by the node and we wouldn't know where to put it. } } else { children.remove(versionedChild); } } for (ChildAssociationRef ref : children) { this.nodeService.removeChild(nodeRef, ref.getChildRef()); } } finally { // Turn auto-version policies back on this.policyBehaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); } } } /* (non-Javadoc) * @see org.alfresco.repo.service.cmr.version.VersionService#restore(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName) */ public NodeRef restore( NodeRef nodeRef, NodeRef parentNodeRef, QName assocTypeQName, QName assocQName) { if (useDeprecatedV1) { return super.restore(nodeRef, parentNodeRef, assocTypeQName, assocQName, true); } return restore(nodeRef, parentNodeRef, assocTypeQName, assocQName, true); } /* (non-Javadoc) * @see org.alfresco.repo.service.cmr.version.VersionService#restore(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, boolean) */ public NodeRef restore( NodeRef nodeRef, NodeRef parentNodeRef, QName assocTypeQName, QName assocQName, boolean deep) { if (useDeprecatedV1) { return super.restore(nodeRef, parentNodeRef, assocTypeQName, assocQName, deep); } NodeRef restoredNodeRef = null; // Check that the node does not exist if (this.nodeService.exists(nodeRef) == true) { // Error since you can not restore a node that already exists throw new VersionServiceException(MSGID_ERR_RESTORE_EXISTS, new Object[]{nodeRef.toString()}); } // Try and get the version details that we want to restore to Version version = getHeadVersion(nodeRef); if (version == null) { // Error since there is no version information available to restore the node from throw new VersionServiceException(MSGID_ERR_RESTORE_NO_VERSION, new Object[]{nodeRef.toString()}); } // Set the uuid of the new node Map props = new HashMap(1); props.put(ContentModel.PROP_NODE_UUID, ((NodeRef)version.getVersionProperty(Version2Model.PROP_FROZEN_NODE_REF)).getId()); // Get the type of the frozen node QName type = (QName)dbNodeService.getType(VersionUtil.convertNodeRef(version.getFrozenStateNodeRef())); // Disable auto-version behaviour this.policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_VERSIONABLE); try { // Create the restored node restoredNodeRef = this.nodeService.createNode( parentNodeRef, assocTypeQName, assocQName, type, props).getChildRef(); } finally { // Enable auto-version behaviour this.policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_VERSIONABLE); } // Now we need to revert the newly restored node revert(restoredNodeRef, version, deep); return restoredNodeRef; } /** * Get the head version given a node reference * * @param nodeRef the node reference * @return the 'head' version */ private Version getHeadVersion(NodeRef nodeRef) { Version version = null; StoreRef storeRef = nodeRef.getStoreRef(); NodeRef versionHistoryNodeRef = getVersionHistoryNodeRef(nodeRef); if (versionHistoryNodeRef != null) { List versionsAssoc = this.dbNodeService.getChildAssocs(versionHistoryNodeRef, Version2Model.CHILD_QNAME_VERSIONS, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef versionAssoc : versionsAssoc) { NodeRef versionNodeRef = versionAssoc.getChildRef(); List successors = this.dbNodeService.getTargetAssocs(versionNodeRef, Version2Model.ASSOC_SUCCESSOR); if (successors.size() == 0) { NodeRef versionedNodeRef = (NodeRef)this.dbNodeService.getProperty( versionNodeRef, QName.createQName(Version2Model.NAMESPACE_URI, Version2Model.PROP_FROZEN_NODE_REF)); StoreRef versionStoreRef = versionedNodeRef.getStoreRef(); if (storeRef.equals(versionStoreRef) == true) { version = getVersion(versionNodeRef); } } } } return version; } /* (non-Javadoc) * @see org.alfresco.repo.service.cmr.version.VersionService#deleteVersionHistory(org.alfresco.service.cmr.repository.NodeRef) */ public void deleteVersionHistory(NodeRef nodeRef) throws AspectMissingException { if (useDeprecatedV1) { super.deleteVersionHistory(nodeRef); } else { // Get the version history node for the node is question and delete it NodeRef versionHistoryNodeRef = getVersionHistoryNodeRef(nodeRef); if (versionHistoryNodeRef != null) { // Delete the version history node this.dbNodeService.deleteNode(versionHistoryNodeRef); if (this.nodeService.exists(nodeRef) == true && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true) { // Reset the version label property on the versionable node this.nodeService.setProperty(nodeRef, ContentModel.PROP_VERSION_LABEL, null); } } } } }