mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
44674: Fix for ALF-17189 - The "Company Home" item in the top navigator menu and in the toolbar panel is invisible, if login as guest first then directly access the login page via URL. 44701: Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/V4.1-BUG-FIX 44700: Fix for ALF-10369 - support for OPTIONS requests for WebScript framework and Share proxy 44709: ALF-17164 Fix version.properties which was wrong in sdk zip 44710: ALF-14570 ("Check out" outboud rule works incorrect) 44722: MNT-246: Need the ability to configure a proxy with Enterprise Sync. - RemoteConnectorServiceImpl will now use an http/https proxy if the standard system properties for Java network proxy configuration are found. See http://docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html (Section 2.1 and 2.2) 44730: Merged V4.1 to V4.1-BUG-FIX 44461: Merged PATCHES/V4.1.1 to V4.1 44060: ALF-16962 / MNT-221 Links from a deleted user cause error in the "Links" page 44129: ALF-17134 / MNT-223: Unbound SOLR result set (from Explorer trashcan query) consumes heap - Now we emulate the behaviour of ACLEntryAfterInvocationProvider in SolrQueryHTTPClient, thus limiting otherwise unconstrained SOLR queries to return a finite number of results - New solr subsystem parameter solr.query.maximumResultsFromUnlimitedQuery introduced - Its default value is ${system.acl.maxPermissionChecks}, thus providing backward compatibility with old behaviour (1000 results max) - When there are no other limits in the search parameters, this value will be used to limit the number of results - SolrJSONResultSet.getResultSetMetata().getLimitedBy() will return an appropriate LimitBy value, according to how the query was limited 44130: ALF-17134 / MNT-223: Unbound SOLR result set (from Explorer trashcan query) consumes heap - Improved calculation of SolrJSONResultSet.getResultSetMetata().getLimitedBy() to better emulate ACLEntryAfterInvocationProvider 44141: ALF-17134 / MNT-223: Unbound SOLR result set (from Explorer trashcan query) consumes heap - Correction to handling of limited queries (Share search works again!) 44291: ALF-17094 / MNT-222 InvalidNodeRefException on user deletion in Share UI 44462: Merged PATCHES/V4.0.2 to V4.1 44221: ALF-17038 / MNT-226: Out-of-order versions for existing data during migration from 3.4.9 to 4.0.2.19 - Have been able to remove the need for any Comparators in the normal case. As Dave said, he thought it was ordered already. It is with "assoc.assoc_index ASC, assoc.id ASC". Required a bit of re factoring of Version2ServiceImpl to do it as they were referenced/used in a couple of other classes. - Modified all 43 Oracle sequences to include ORDER in the create statement. Probably only really was needed to do it on alf_child_assoc_seq to fix this issue, but it will stop similar issues in other clustered database setups. Did not change the upgrade scripts, as this will give us a clue that there will be existing data issues. - The name of a Comparator<Version> may be specified in the Alfresco global property: org.alfresco.repo.version.common.VersionLabelComparator and it will be used by BOTH Version2ServiceImpl and VersionServiceImpl. They in turn pass it on to Version2ServiceImpl instances when they create them. - A VersionLabelComparator already existed (still deprecated as we don't normally use it) and works: org.alfresco.repo.version.common.VersionLabelComparator. - Customers with out of sequence ids on Oracle RDBMS using a clustered database may 'correct on the fly' the order of their versions by setting the alfresco global property described above. - Have tested both with and without a comparator in development environment. Using break points and Collections.shuffle(version) in an expression was able to simulate out of order IDs. - New unit tests added to VersionHistoryImplTest and VersionServiceImplTest to test db ids out of order 44336: ALF-15935: Fixed SecureContext errors when ticket has expired. MNT-180 44467: Fixed compilation failure 44520: ALF-16590: Improved fix after testing by Mark Lugert 44563: Merged DEV to V4.1 (with corrections) 44547: ALF-17132: Possible XSS - arbitrary url parameters re-sent to the browser Escaping of keys and values of request attributes 44610: Merged PATCHES/V4.0.2 to V4.1 44435: ALF-17183: Merged DEV to V4.0.2 (4.0.2.22) 44429: MNT-232: Upgrade from 3.4.9 to 4.0.2 - FAILED - Initialize rootRefs in the property definition to prevent NPE. 44591: Fix to CIFS reported user free space when disk quotas are not enabled. 44595: ALF-17184 / MNT-243 Minimal fix for disk size and user quotas. (Bring values into line with API.) 44601: ALF-17184 / MNT-243 - Implementation of file size on Abstract Tennant Routing Content Store. 44608: ALF-15935 / MNT-180: Moved closeFile() call to closeConnection() cleanup method, always call closeFile() Do not check if file is marked as closed during cleanup, only open files should still be in the file table. 44652: ALF-17117: Created article or publication cant be viewed on WQS site - Fixes by Dmitry Vaserin - Removed unnecessary outer read locks from getRelatedAssets and getRelatedAsset to prevent deadlock - Correct markup error when node doesn't have tags 44653: ALF-17117: Created article or publication cant be viewed on WQS site - Missed file from previous checkin 44682: ALF-17118 WQS: Impossible to upload document to publications space - Only first part to do with the transformation failure has been committed. 44731: Merged V4.1 to V4.1-BUG-FIX (RECORD ONLY) 44441: Merge V4.1-BUG-FIX to V4.1 44270: Merge V3.4-BUG-FIX to V4.1-BUG-FIX 44266: BDE-111: harden generation of Windows installers - make sure build fails if installer generation fails - generate Windows unsigned installers in a place that is cleaned later, avoiding leftovers 44598: Merged V4.1-BUG-FIX to V4.1 44541: Fix for ALF-17151 SOLR - add support to disable permission checks 44577: Final part for ALF-16558 SOLR tracking does not do incremental updates but one single chunk - fixed code so SolrSearchers are held for as little time as possible 44607: Merged V4.1-BUG-FIX to V4.1 44603: ALF-14201: upgrade activiti to 5.7-20121211 44606: ALF-14201: upgrade activiti to 5.7-20121211 in Maven poms git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@44732 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
1358 lines
55 KiB
Java
1358 lines
55 KiB
Java
/*
|
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
|
*
|
|
* This file is part of Alfresco
|
|
*
|
|
* Alfresco is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Alfresco 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 Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package org.alfresco.repo.version;
|
|
|
|
import java.io.Serializable;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.Comparator;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import org.alfresco.error.AlfrescoRuntimeException;
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.repo.node.MLPropertyInterceptor;
|
|
import org.alfresco.repo.policy.BehaviourFilter;
|
|
import org.alfresco.repo.policy.JavaBehaviour;
|
|
import org.alfresco.repo.policy.PolicyScope;
|
|
import org.alfresco.repo.version.VersionServicePolicies.CalculateVersionLabelPolicy;
|
|
import org.alfresco.repo.version.common.AbstractVersionServiceImpl;
|
|
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.dictionary.AspectDefinition;
|
|
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
|
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
|
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
|
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.NodeService;
|
|
import org.alfresco.service.cmr.repository.StoreRef;
|
|
import org.alfresco.service.cmr.search.SearchService;
|
|
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.cmr.version.VersionType;
|
|
import org.alfresco.service.namespace.NamespaceService;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.service.namespace.RegexQNamePattern;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.springframework.extensions.surf.util.ParameterCheck;
|
|
|
|
/**
|
|
* Version1 Service - implements lightWeightVersionStore
|
|
*
|
|
* @author Roy Wetheral
|
|
*
|
|
* NOTE: deprecated since 3.1 (migrate and use Version2 Service)
|
|
*/
|
|
public class VersionServiceImpl extends AbstractVersionServiceImpl implements VersionService, VersionModel
|
|
{
|
|
private static Log logger = LogFactory.getLog(VersionServiceImpl.class);
|
|
|
|
/**
|
|
* Error message I18N id's
|
|
*/
|
|
protected static final String MSGID_ERR_NOT_FOUND = "version_service.err_not_found";
|
|
protected static final String MSGID_ERR_NO_BRANCHES = "version_service.err_unsupported";
|
|
protected static final String MSGID_ERR_RESTORE_EXISTS = "version_service.err_restore_exists";
|
|
protected static final String MSGID_ERR_ONE_PRECEEDING = "version_service.err_one_preceeding";
|
|
protected static final String MSGID_ERR_RESTORE_NO_VERSION = "version_service.err_restore_no_version";
|
|
protected static final String MSGID_ERR_REVERT_MISMATCH = "version_service.err_revert_mismatch";
|
|
|
|
/**
|
|
* The db node service, used as the version store implementation
|
|
*/
|
|
protected NodeService dbNodeService;
|
|
|
|
/**
|
|
* Policy behaviour filter
|
|
*/
|
|
protected BehaviourFilter policyBehaviourFilter;
|
|
|
|
/**
|
|
* The repository searcher
|
|
*/
|
|
protected SearchService searcher; // unused
|
|
|
|
protected Comparator<Version> versionComparatorDesc;
|
|
|
|
/**
|
|
* Sets the db node service, used as the version store implementation
|
|
*
|
|
* @param nodeService the node service
|
|
*/
|
|
public void setDbNodeService(NodeService nodeService)
|
|
{
|
|
this.dbNodeService = nodeService;
|
|
}
|
|
|
|
/**
|
|
* @param searcher the searcher
|
|
*/
|
|
public void setSearcher(SearchService searcher)
|
|
{
|
|
this.searcher = searcher;
|
|
}
|
|
|
|
/**
|
|
* Set the policy behaviour filter
|
|
*
|
|
* @param policyBehaviourFilter the policy behaviour filter
|
|
*/
|
|
public void setPolicyBehaviourFilter(BehaviourFilter policyBehaviourFilter)
|
|
{
|
|
this.policyBehaviourFilter = policyBehaviourFilter;
|
|
}
|
|
|
|
/**
|
|
* Sets an optional comparator to sort a versions in descending order (eg. 2.1, 2.0, 1.1, 1.0).
|
|
* Really only needed in a 4.1.3 system (or above) that has been upgraded from an earlier system
|
|
* that used NON ordered sequence numbers in a cluster. Not something we really support but was
|
|
* the subject of MNT-226.
|
|
* @param versionComparatorClass the name of a comparator. For example
|
|
* "org.alfresco.repo.version.common.VersionLabelComparator".
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public void setVersionComparatorClass(String versionComparatorClass)
|
|
{
|
|
if (versionComparatorClass != null && versionComparatorClass.trim().length() != 0)
|
|
{
|
|
try
|
|
{
|
|
versionComparatorDesc = (Comparator<Version>) getClass().getClassLoader().
|
|
loadClass(versionComparatorClass.trim()).newInstance();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new AlfrescoRuntimeException(
|
|
"Failed to create a Comparator<Version> using the class name "+
|
|
versionComparatorClass, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register version label policy for the specified type
|
|
*
|
|
* @param typeQName
|
|
* @param policy
|
|
*/
|
|
public void registerVersionLabelPolicy(QName typeQName, CalculateVersionLabelPolicy policy)
|
|
{
|
|
// Register the serial version label behaviour
|
|
this.policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "calculateVersionLabel"),
|
|
typeQName,
|
|
new JavaBehaviour(policy, "calculateVersionLabel"));
|
|
}
|
|
|
|
/**
|
|
* Initialise method
|
|
*/
|
|
@Override
|
|
public void initialise()
|
|
{
|
|
super.initialise();
|
|
}
|
|
|
|
// TODO - temp
|
|
protected void initialiseWithoutBind()
|
|
{
|
|
super.initialise();
|
|
}
|
|
|
|
/**
|
|
* Gets the reference to the version store
|
|
*
|
|
* @return reference to the version store
|
|
*/
|
|
@Override
|
|
public StoreRef getVersionStoreReference()
|
|
{
|
|
return new StoreRef(
|
|
StoreRef.PROTOCOL_WORKSPACE,
|
|
VersionModel.STORE_ID);
|
|
}
|
|
|
|
/**
|
|
* @see VersionCounterService#nextVersionNumber(StoreRef)
|
|
*/
|
|
public Version createVersion(
|
|
NodeRef nodeRef,
|
|
Map<String, Serializable> versionProperties)
|
|
throws ReservedVersionNameException, AspectMissingException
|
|
{
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
int versionNumber = 0; // deprecated (unused)
|
|
|
|
// Create the version
|
|
Version version = createVersion(nodeRef, versionProperties, versionNumber);
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("created version (" + VersionUtil.convertNodeRef(version.getFrozenStateNodeRef()) + ") in " + (System.currentTimeMillis()-startTime) + " ms");
|
|
}
|
|
|
|
return version;
|
|
}
|
|
|
|
/**
|
|
* The version's are created from the children upwards with the parent being created first. This will
|
|
* ensure that the child version references in the version node will point to the version history nodes
|
|
* for the (possibly) newly created version histories.
|
|
*/
|
|
public Collection<Version> createVersion(
|
|
NodeRef nodeRef,
|
|
Map<String, Serializable> versionProperties,
|
|
boolean versionChildren)
|
|
throws ReservedVersionNameException, AspectMissingException
|
|
{
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
int versionNumber = 0; // deprecated (unused)
|
|
|
|
// Create the versions
|
|
Collection<Version> versions = createVersion(nodeRef, versionProperties, versionChildren, versionNumber);
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
Version[] versionsArray = versions.toArray(new Version[0]);
|
|
Version version = versionsArray[versionsArray.length -1]; // last item is the new parent version
|
|
logger.debug("created version (" + VersionUtil.convertNodeRef(version.getFrozenStateNodeRef()) + ") in "+ (System.currentTimeMillis()-startTime) +" ms "+(versionChildren ? "(with " + (versions.size() - 1) + " children)" : ""));
|
|
}
|
|
|
|
return versions;
|
|
}
|
|
|
|
/**
|
|
* Helper method used to create the version when the versionChildren flag is provided. This method
|
|
* ensures that all the children (if the falg is set to true) are created with the same version
|
|
* number, this ensuring that the version stripe is correct.
|
|
*
|
|
* @param nodeRef the parent node reference
|
|
* @param versionProperties the version properties
|
|
* @param versionChildren indicates whether to version the children of the parent
|
|
* node
|
|
* @param versionNumber the version number
|
|
|
|
* @return a collection of the created versions
|
|
* @throws ReservedVersionNameException thrown if there is a reserved version property name clash
|
|
* @throws AspectMissingException thrown if the version aspect is missing from a node
|
|
*/
|
|
private Collection<Version> createVersion(
|
|
NodeRef nodeRef,
|
|
Map<String, Serializable> versionProperties,
|
|
boolean versionChildren,
|
|
int versionNumber)
|
|
throws ReservedVersionNameException, AspectMissingException
|
|
{
|
|
|
|
Collection<Version> result = new ArrayList<Version>();
|
|
|
|
if (versionChildren == true)
|
|
{
|
|
// Get the children of the node
|
|
Collection<ChildAssociationRef> children = this.dbNodeService.getChildAssocs(nodeRef);
|
|
for (ChildAssociationRef childAssoc : children)
|
|
{
|
|
// Recurse into this method to version all the children with the same version number
|
|
Collection<Version> childVersions = createVersion(
|
|
childAssoc.getChildRef(),
|
|
versionProperties,
|
|
versionChildren,
|
|
versionNumber);
|
|
result.addAll(childVersions);
|
|
}
|
|
}
|
|
|
|
result.add(createVersion(nodeRef, versionProperties, versionNumber));
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 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 verison history.
|
|
*/
|
|
public Collection<Version> createVersion(
|
|
Collection<NodeRef> nodeRefs,
|
|
Map<String, Serializable> versionProperties)
|
|
throws ReservedVersionNameException, AspectMissingException
|
|
{
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
Collection<Version> result = new ArrayList<Version>(nodeRefs.size());
|
|
|
|
int versionNumber = 0; // deprecated (unused)
|
|
|
|
// Version each node in the list
|
|
for (NodeRef nodeRef : nodeRefs)
|
|
{
|
|
result.add(createVersion(nodeRef, versionProperties, versionNumber));
|
|
}
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("created version list (" + getVersionStoreReference() + ") in "+ (System.currentTimeMillis()-startTime) +" ms (with " + nodeRefs.size() + " nodes)");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Creates a new version of the passed node assigning the version properties
|
|
* accordingly.
|
|
*
|
|
* @param nodeRef a node reference
|
|
* @param versionProperties the version properties
|
|
* @param versionNumber the version number
|
|
* @return the newly created version
|
|
* @throws ReservedVersionNameException
|
|
* thrown if there is a name clash in the version properties
|
|
*/
|
|
protected Version createVersion(
|
|
NodeRef nodeRef,
|
|
Map<String, Serializable> origVersionProperties,
|
|
int versionNumber)
|
|
throws ReservedVersionNameException
|
|
{
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
// Copy the version properties (to prevent unexpected side effects to the caller)
|
|
Map<String, Serializable> versionProperties = new HashMap<String, Serializable>();
|
|
if (origVersionProperties != null)
|
|
{
|
|
versionProperties.putAll(origVersionProperties);
|
|
}
|
|
|
|
// If the version aspect is not there then add it
|
|
if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == false)
|
|
{
|
|
this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, null);
|
|
}
|
|
|
|
// Call the policy behaviour
|
|
invokeBeforeCreateVersion(nodeRef);
|
|
|
|
// 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 exisiting 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);
|
|
|
|
// Create the new version node (child of the version history)
|
|
NodeRef newVersionRef = createNewVersion(
|
|
nodeRef,
|
|
versionHistoryRef,
|
|
getStandardVersionProperties(versionProperties, nodeRef, currentVersionRef, versionNumber),
|
|
versionProperties,
|
|
nodeDetails);
|
|
|
|
if (currentVersionRef == null)
|
|
{
|
|
// Set the new version to be the root version in the version history
|
|
this.dbNodeService.createAssociation(
|
|
versionHistoryRef,
|
|
newVersionRef,
|
|
VersionServiceImpl.ASSOC_ROOT_VERSION);
|
|
}
|
|
else
|
|
{
|
|
// Relate the new version to the current version as its successor
|
|
this.dbNodeService.createAssociation(
|
|
currentVersionRef,
|
|
newVersionRef,
|
|
VersionServiceImpl.ASSOC_SUCCESSOR);
|
|
}
|
|
|
|
// Create the version data object
|
|
Version version = this.getVersion(newVersionRef);
|
|
|
|
// Set the new version label on the versioned node
|
|
this.nodeService.setProperty(
|
|
nodeRef,
|
|
ContentModel.PROP_VERSION_LABEL,
|
|
version.getVersionLabel());
|
|
|
|
// Freeze the version label property
|
|
Map<QName, Serializable> versionLabelAsMap = new HashMap<QName, Serializable>(1);
|
|
versionLabelAsMap.put(ContentModel.PROP_VERSION_LABEL, version.getVersionLabel());
|
|
this.freezeProperties(newVersionRef, versionLabelAsMap);
|
|
|
|
// Invoke the policy behaviour
|
|
invokeAfterCreateVersion(nodeRef, version);
|
|
|
|
if (logger.isTraceEnabled())
|
|
{
|
|
logger.trace("created Version (" + getVersionStoreReference() + ") " + nodeRef + " in " + (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
|
|
*/
|
|
private NodeRef createVersionHistory(NodeRef nodeRef)
|
|
{
|
|
HashMap<QName, Serializable> props = new HashMap<QName, Serializable>();
|
|
props.put(ContentModel.PROP_NAME, nodeRef.getId());
|
|
props.put(PROP_QNAME_VERSIONED_NODE_ID, nodeRef.getId());
|
|
|
|
// Create a new version history node
|
|
ChildAssociationRef childAssocRef = this.dbNodeService.createNode(
|
|
getRootNode(),
|
|
CHILD_QNAME_VERSION_HISTORIES,
|
|
QName.createQName(VersionModel.NAMESPACE_URI, nodeRef.getId()),
|
|
TYPE_QNAME_VERSION_HISTORY,
|
|
props);
|
|
return childAssocRef.getChildRef();
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.version.VersionService#getVersionHistory(NodeRef)
|
|
*/
|
|
public VersionHistory getVersionHistory(NodeRef nodeRef)
|
|
{
|
|
VersionHistory versionHistory = null;
|
|
|
|
if (this.nodeService.exists(nodeRef) == true)
|
|
{
|
|
NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef);
|
|
if (versionHistoryRef != null)
|
|
{
|
|
versionHistory = buildVersionHistory(versionHistoryRef, nodeRef);
|
|
}
|
|
}
|
|
|
|
return versionHistory;
|
|
}
|
|
|
|
/**
|
|
* @see VersionService#getCurrentVersion(NodeRef)
|
|
*/
|
|
public Version getCurrentVersion(NodeRef nodeRef)
|
|
{
|
|
Version version = null;
|
|
|
|
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 versionProperties the version meta data properties
|
|
* @param nodeRef the node reference
|
|
* @param preceedingNodeRef the preceeding node reference
|
|
* @param versionNumber the version number
|
|
* @return the standard version properties
|
|
*/
|
|
private Map<QName, Serializable> getStandardVersionProperties(Map<String, Serializable> versionProperties, NodeRef nodeRef, NodeRef preceedingNodeRef, int versionNumber)
|
|
{
|
|
Map<QName, Serializable> result = new HashMap<QName, Serializable>(10);
|
|
|
|
// deprecated (unused)
|
|
//result.put(VersionModel.PROP_QNAME_VERSION_NUMBER, Integer.toString(versionNumber));
|
|
|
|
// Set the versionable node id
|
|
result.put(VersionModel.PROP_QNAME_FROZEN_NODE_ID, nodeRef.getId());
|
|
|
|
// Set the versionable node store protocol
|
|
result.put(VersionModel.PROP_QNAME_FROZEN_NODE_STORE_PROTOCOL, nodeRef.getStoreRef().getProtocol());
|
|
|
|
// Set the versionable node store id
|
|
result.put(VersionModel.PROP_QNAME_FROZEN_NODE_STORE_ID, nodeRef.getStoreRef().getIdentifier());
|
|
|
|
// Store the current node type
|
|
QName nodeType = this.nodeService.getType(nodeRef);
|
|
result.put(VersionModel.PROP_QNAME_FROZEN_NODE_TYPE, nodeType);
|
|
|
|
// Store the current aspects
|
|
Set<QName> aspects = this.nodeService.getAspects(nodeRef);
|
|
result.put(VersionModel.PROP_QNAME_FROZEN_ASPECTS, (Serializable)aspects);
|
|
|
|
// Calculate the version label
|
|
QName classRef = this.nodeService.getType(nodeRef);
|
|
Version preceedingVersion = getVersion(preceedingNodeRef);
|
|
String versionLabel = invokeCalculateVersionLabel(classRef, preceedingVersion, versionNumber, versionProperties);
|
|
result.put(VersionModel.PROP_QNAME_VERSION_LABEL, versionLabel);
|
|
|
|
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
|
|
*/
|
|
private NodeRef createNewVersion(
|
|
NodeRef versionableNodeRef,
|
|
NodeRef versionHistoryRef,
|
|
Map<QName, Serializable> standardVersionProperties,
|
|
Map<String, Serializable> versionProperties,
|
|
PolicyScope nodeDetails)
|
|
{
|
|
// Create the new version
|
|
ChildAssociationRef childAssocRef = this.dbNodeService.createNode(
|
|
versionHistoryRef,
|
|
CHILD_QNAME_VERSIONS,
|
|
CHILD_QNAME_VERSIONS,
|
|
TYPE_QNAME_VERSION,
|
|
standardVersionProperties);
|
|
NodeRef versionNodeRef = childAssocRef.getChildRef();
|
|
|
|
// Store the meta data
|
|
storeVersionMetaData(versionNodeRef, versionProperties);
|
|
|
|
// Freeze the various parts of the node
|
|
freezeProperties(versionNodeRef, nodeDetails.getProperties());
|
|
freezeChildAssociations(versionNodeRef, nodeDetails.getChildAssociations());
|
|
freezeAssociations(versionNodeRef, nodeDetails.getAssociations());
|
|
freezeAspects(nodeDetails, versionNodeRef, nodeDetails.getAspects());
|
|
|
|
// 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<String, Serializable> versionProperties)
|
|
{
|
|
for (Map.Entry<String, Serializable> entry : versionProperties.entrySet())
|
|
{
|
|
HashMap<QName, Serializable> properties = new HashMap<QName, Serializable>();
|
|
|
|
properties.put(PROP_QNAME_META_DATA_NAME, entry.getKey());
|
|
properties.put(PROP_QNAME_META_DATA_VALUE, entry.getValue());
|
|
|
|
this.dbNodeService.createNode(
|
|
versionNodeRef,
|
|
CHILD_QNAME_VERSION_META_DATA,
|
|
CHILD_QNAME_VERSION_META_DATA,
|
|
TYPE_QNAME_VERSION_META_DATA_VALUE,
|
|
properties);
|
|
}
|
|
}
|
|
|
|
protected Map<String, Serializable> getVersionMetaData(NodeRef versionNodeRef)
|
|
{
|
|
// Get the meta data
|
|
List<ChildAssociationRef> metaData = this.dbNodeService.getChildAssocs(
|
|
versionNodeRef,
|
|
RegexQNamePattern.MATCH_ALL,
|
|
CHILD_QNAME_VERSION_META_DATA);
|
|
|
|
Map<String, Serializable> versionProperties = new HashMap<String, Serializable>(metaData.size());
|
|
|
|
for (ChildAssociationRef ref : metaData)
|
|
{
|
|
NodeRef metaDataValue = (NodeRef)ref.getChildRef();
|
|
String name = (String)this.dbNodeService.getProperty(metaDataValue, PROP_QNAME_META_DATA_NAME);
|
|
Serializable value = this.dbNodeService.getProperty(metaDataValue, PROP_QNAME_META_DATA_VALUE);
|
|
versionProperties.put(name, value);
|
|
}
|
|
|
|
return versionProperties;
|
|
}
|
|
|
|
/**
|
|
* 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<QName> aspects)
|
|
{
|
|
for (QName aspect : aspects)
|
|
{
|
|
// Freeze the details of the aspect
|
|
freezeProperties(versionNodeRef, nodeDetails.getProperties(aspect));
|
|
freezeChildAssociations(versionNodeRef, nodeDetails.getChildAssociations(aspect));
|
|
freezeAssociations(versionNodeRef, nodeDetails.getAssociations(aspect));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Freeze associations
|
|
*
|
|
* @param versionNodeRef the version node reference
|
|
* @param associations the list of associations
|
|
*/
|
|
private void freezeAssociations(NodeRef versionNodeRef, List<AssociationRef> associations)
|
|
{
|
|
for (AssociationRef targetAssoc : associations)
|
|
{
|
|
HashMap<QName, Serializable> properties = new HashMap<QName, Serializable>();
|
|
|
|
// Set the qname of the association
|
|
properties.put(PROP_QNAME_ASSOC_TYPE_QNAME, targetAssoc.getTypeQName());
|
|
|
|
// Set the reference property to point to the child node
|
|
properties.put(ContentModel.PROP_REFERENCE, targetAssoc.getTargetRef());
|
|
|
|
// Create child version reference
|
|
this.dbNodeService.createNode(
|
|
versionNodeRef,
|
|
CHILD_QNAME_VERSIONED_ASSOCS,
|
|
CHILD_QNAME_VERSIONED_ASSOCS,
|
|
TYPE_QNAME_VERSIONED_ASSOC,
|
|
properties);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Freeze child associations
|
|
*
|
|
* @param versionNodeRef the version node reference
|
|
* @param childAssociations the child associations
|
|
*/
|
|
private void freezeChildAssociations(NodeRef versionNodeRef, List<ChildAssociationRef> childAssociations)
|
|
{
|
|
for (ChildAssociationRef childAssocRef : childAssociations)
|
|
{
|
|
HashMap<QName, Serializable> properties = new HashMap<QName, Serializable>();
|
|
|
|
// Set the qname, isPrimary and nthSibling properties
|
|
properties.put(PROP_QNAME_ASSOC_QNAME, childAssocRef.getQName());
|
|
properties.put(PROP_QNAME_ASSOC_TYPE_QNAME, childAssocRef.getTypeQName());
|
|
properties.put(PROP_QNAME_IS_PRIMARY, Boolean.valueOf(childAssocRef.isPrimary()));
|
|
properties.put(PROP_QNAME_NTH_SIBLING, Integer.valueOf(childAssocRef.getNthSibling()));
|
|
|
|
// 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,
|
|
CHILD_QNAME_VERSIONED_CHILD_ASSOCS,
|
|
CHILD_QNAME_VERSIONED_CHILD_ASSOCS,
|
|
TYPE_QNAME_VERSIONED_CHILD_ASSOC,
|
|
properties);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Freeze properties
|
|
*
|
|
* @param versionNodeRef the version node reference
|
|
* @param properties the properties
|
|
*/
|
|
private void freezeProperties(NodeRef versionNodeRef, Map<QName, Serializable> properties)
|
|
{
|
|
// Copy the property values from the node onto the version node
|
|
for (Map.Entry<QName, Serializable> entry : properties.entrySet())
|
|
{
|
|
// Get the property values
|
|
HashMap<QName, Serializable> props = new HashMap<QName, Serializable>();
|
|
props.put(PROP_QNAME_QNAME, entry.getKey());
|
|
|
|
if (entry.getValue() instanceof Collection)
|
|
{
|
|
props.put(PROP_QNAME_MULTI_VALUE, entry.getValue());
|
|
props.put(PROP_QNAME_IS_MULTI_VALUE, true);
|
|
}
|
|
else
|
|
{
|
|
props.put(PROP_QNAME_VALUE, entry.getValue());
|
|
props.put(PROP_QNAME_IS_MULTI_VALUE, false);
|
|
}
|
|
|
|
// Create the node storing the frozen attribute details
|
|
this.dbNodeService.createNode(
|
|
versionNodeRef,
|
|
CHILD_QNAME_VERSIONED_ATTRIBUTES,
|
|
CHILD_QNAME_VERSIONED_ATTRIBUTES,
|
|
TYPE_QNAME_VERSIONED_PROPERTY,
|
|
props);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the version stores root node
|
|
*
|
|
* @return the node ref to the root node of the version store
|
|
*/
|
|
protected NodeRef getRootNode()
|
|
{
|
|
// Get the version store root node reference
|
|
return this.dbNodeService.getRootNode(getVersionStoreReference());
|
|
}
|
|
|
|
/**
|
|
* Builds a version history object from the version history reference.
|
|
* <p>
|
|
* 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)
|
|
{
|
|
VersionHistory versionHistory = null;
|
|
|
|
// List of versions with current one last and root one first.
|
|
ArrayList<NodeRef> versionHistoryNodeRefs = new ArrayList<NodeRef>();
|
|
|
|
NodeRef currentVersion;
|
|
if (this.nodeService.exists(nodeRef))
|
|
{
|
|
currentVersion = getCurrentVersionNodeRef(versionHistoryRef, nodeRef);
|
|
}
|
|
else
|
|
{
|
|
currentVersion = VersionUtil.convertNodeRef(getLatestVersion(nodeRef).getFrozenStateNodeRef());
|
|
}
|
|
|
|
while (currentVersion != null)
|
|
{
|
|
AssociationRef preceedingVersion = null;
|
|
|
|
versionHistoryNodeRefs.add(0, currentVersion);
|
|
|
|
List<AssociationRef> preceedingVersions = this.dbNodeService.getSourceAssocs(
|
|
currentVersion,
|
|
VersionModel.ASSOC_SUCCESSOR);
|
|
if (preceedingVersions.size() == 1)
|
|
{
|
|
preceedingVersion = (AssociationRef)preceedingVersions.toArray()[0];
|
|
currentVersion = preceedingVersion.getSourceRef();
|
|
}
|
|
else if (preceedingVersions.size() > 1)
|
|
{
|
|
// Error since we only currently support one preceeding version
|
|
throw new VersionServiceException(MSGID_ERR_ONE_PRECEEDING);
|
|
}
|
|
else
|
|
{
|
|
currentVersion = null;
|
|
}
|
|
}
|
|
|
|
// Build the version history object
|
|
boolean isRoot = true;
|
|
Version preceeding = null;
|
|
for (NodeRef versionRef : versionHistoryNodeRefs)
|
|
{
|
|
Version version = getVersion(versionRef);
|
|
|
|
if (isRoot == true)
|
|
{
|
|
versionHistory = new VersionHistoryImpl(version, versionComparatorDesc);
|
|
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 (versionRef == null)
|
|
{
|
|
return null;
|
|
}
|
|
Map<String, Serializable> versionProperties = new HashMap<String, Serializable>();
|
|
|
|
// Get the standard node details
|
|
Map<QName, Serializable> nodeProperties = this.dbNodeService.getProperties(versionRef);
|
|
for (QName key : nodeProperties.keySet())
|
|
{
|
|
Serializable value = nodeProperties.get(key);
|
|
versionProperties.put(key.getLocalName(), value);
|
|
}
|
|
|
|
// Get the meta data
|
|
Map<String, Serializable> versionMetaDataProperties = getVersionMetaData(versionRef);
|
|
versionProperties.putAll(versionMetaDataProperties);
|
|
|
|
// Create and return the version object
|
|
NodeRef newNodeRef = new NodeRef(new StoreRef(STORE_PROTOCOL, 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 (nodeService.exists(nodeRef))
|
|
{
|
|
return this.dbNodeService.getChildByName(getRootNode(), CHILD_QNAME_VERSION_HISTORIES, (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_UUID));
|
|
}
|
|
else
|
|
{
|
|
return this.dbNodeService.getChildByName(getRootNode(), 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<ChildAssociationRef> versions = this.dbNodeService.getChildAssocs(versionHistory);
|
|
for (ChildAssociationRef version : versions)
|
|
{
|
|
String tempLabel = (String)this.dbNodeService.getProperty(version.getChildRef(), VersionModel.PROP_QNAME_VERSION_LABEL);
|
|
if (tempLabel != null && tempLabel.equals(versionLabel) == true)
|
|
{
|
|
result = version.getChildRef();
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.cms.version.VersionService#ensureVersioningEnabled(NodeRef,Map)
|
|
*/
|
|
public void ensureVersioningEnabled(NodeRef nodeRef, Map<QName, Serializable> versionProperties)
|
|
{
|
|
// Don't alter the auditable aspect!
|
|
boolean disableAuditable = policyBehaviourFilter.isEnabled(ContentModel.ASPECT_AUDITABLE);
|
|
if(disableAuditable)
|
|
{
|
|
policyBehaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
|
|
}
|
|
|
|
// Do we need to apply the aspect?
|
|
if (! nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE))
|
|
{
|
|
// Only apply new properties that are version ones
|
|
AspectDefinition versionable =
|
|
dictionaryService.getAspect(ContentModel.ASPECT_VERSIONABLE);
|
|
Set<QName> versionAspectProperties =
|
|
versionable.getProperties().keySet();
|
|
|
|
Map<QName,Serializable> props = new HashMap<QName, Serializable>();
|
|
if(versionProperties != null &&! versionProperties.isEmpty())
|
|
{
|
|
for(QName prop : versionProperties.keySet())
|
|
{
|
|
if(versionAspectProperties.contains(prop))
|
|
{
|
|
// This property is one from the versionable aspect
|
|
props.put(prop, versionProperties.get(prop));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the aspect
|
|
nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, props);
|
|
}
|
|
|
|
// Do we need to create the initial version history entry? By convention this is always a major version.
|
|
if(getVersionHistory(nodeRef) == null)
|
|
{
|
|
createVersion(nodeRef, Collections.<String,Serializable>singletonMap(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR));
|
|
}
|
|
|
|
// Put Auditable back
|
|
if(disableAuditable)
|
|
{
|
|
policyBehaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.cms.version.VersionService#revert(NodeRef)
|
|
*/
|
|
public void revert(NodeRef nodeRef)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
// 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(version.getVersionProperty(VersionModel.PROP_FROZEN_NODE_ID)) == 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
|
|
this.nodeService.setProperties(nodeRef, this.nodeService.getProperties(versionNodeRef));
|
|
|
|
// Apply/remove the aspects as required
|
|
Set<QName> aspects = new HashSet<QName>(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 origional)
|
|
this.nodeService.setProperty(nodeRef, ContentModel.PROP_VERSION_LABEL, currentVersionLabel);
|
|
|
|
// Add/remove the child nodes
|
|
List<ChildAssociationRef> children = new ArrayList<ChildAssociationRef>(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());
|
|
}
|
|
|
|
// Add/remove the target associations
|
|
for (AssociationRef assocRef : this.nodeService.getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL))
|
|
{
|
|
this.nodeService.removeAssociation(assocRef.getSourceRef(), assocRef.getTargetRef(), assocRef.getTypeQName());
|
|
}
|
|
for (AssociationRef versionedAssoc : this.nodeService.getTargetAssocs(versionNodeRef, RegexQNamePattern.MATCH_ALL))
|
|
{
|
|
if (this.nodeService.exists(versionedAssoc.getTargetRef()) == true)
|
|
{
|
|
this.nodeService.createAssociation(nodeRef, versionedAssoc.getTargetRef(), versionedAssoc.getTypeQName());
|
|
}
|
|
|
|
// else
|
|
// Since the tareget of the assoc no longer exists we can't recreate the assoc
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
// Turn auto-version policies back on
|
|
this.policyBehaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.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)
|
|
{
|
|
return restore(nodeRef, parentNodeRef, assocTypeQName, assocQName, true);
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.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)
|
|
{
|
|
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<QName, Serializable> props = new HashMap<QName, Serializable>(1);
|
|
props.put(ContentModel.PROP_NODE_UUID, version.getVersionProperty(VersionModel.PROP_FROZEN_NODE_ID));
|
|
|
|
// Get the type of the node node
|
|
QName type = (QName)version.getVersionProperty(VersionModel.PROP_FROZEN_NODE_TYPE);
|
|
|
|
// 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<ChildAssociationRef> versionsAssoc = this.dbNodeService.getChildAssocs(versionHistoryNodeRef, RegexQNamePattern.MATCH_ALL, VersionModel.CHILD_QNAME_VERSIONS);
|
|
for (ChildAssociationRef versionAssoc : versionsAssoc)
|
|
{
|
|
NodeRef versionNodeRef = versionAssoc.getChildRef();
|
|
List<AssociationRef> successors = this.dbNodeService.getTargetAssocs(versionNodeRef, VersionModel.ASSOC_SUCCESSOR);
|
|
if (successors.size() == 0)
|
|
{
|
|
String storeProtocol = (String)this.dbNodeService.getProperty(
|
|
versionNodeRef,
|
|
QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_PROTOCOL));
|
|
String storeId = (String)this.dbNodeService.getProperty(
|
|
versionNodeRef,
|
|
QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_ID));
|
|
StoreRef versionStoreRef = new StoreRef(storeProtocol, storeId);
|
|
if (storeRef.equals(versionStoreRef) == true)
|
|
{
|
|
version = getVersion(versionNodeRef);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return version;
|
|
}
|
|
|
|
private Version getLatestVersion(NodeRef nodeRef)
|
|
{
|
|
Version version = null;
|
|
StoreRef storeRef = nodeRef.getStoreRef();
|
|
|
|
NodeRef versionHistoryNodeRef = getVersionHistoryNodeRef(nodeRef);
|
|
if (versionHistoryNodeRef != null)
|
|
{
|
|
List<ChildAssociationRef> versionsAssoc = this.dbNodeService.getChildAssocs(versionHistoryNodeRef, RegexQNamePattern.MATCH_ALL, VersionModel.CHILD_QNAME_VERSIONS);
|
|
for (ChildAssociationRef versionAssoc : versionsAssoc)
|
|
{
|
|
NodeRef versionNodeRef = versionAssoc.getChildRef();
|
|
List<AssociationRef> predecessors = this.dbNodeService.getSourceAssocs(versionNodeRef, VersionModel.ASSOC_SUCCESSOR);
|
|
if (predecessors.size() == 0)
|
|
{
|
|
String storeProtocol = (String)this.dbNodeService.getProperty(
|
|
versionNodeRef,
|
|
QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_PROTOCOL));
|
|
String storeId = (String)this.dbNodeService.getProperty(
|
|
versionNodeRef,
|
|
QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_ID));
|
|
StoreRef versionStoreRef = new StoreRef(storeProtocol, storeId);
|
|
if (storeRef.equals(versionStoreRef) == true)
|
|
{
|
|
version = getVersion(versionNodeRef);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return version;
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.cms.version.VersionService#deleteVersionHistory(NodeRef)
|
|
*/
|
|
public void deleteVersionHistory(NodeRef nodeRef)
|
|
throws AspectMissingException
|
|
{
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void deleteVersion(NodeRef nodeRef, Version version)
|
|
{
|
|
// This operation is not supported for a version1 store
|
|
throw new UnsupportedOperationException("Delete version is unsupported by the old (deprecated) version store implementation");
|
|
}
|
|
|
|
@Override
|
|
protected void defaultOnCreateVersion(
|
|
QName classRef,
|
|
NodeRef nodeRef,
|
|
Map<String, Serializable> versionProperties,
|
|
PolicyScope nodeDetails)
|
|
{
|
|
ClassDefinition classDefinition = this.dictionaryService.getClass(classRef);
|
|
if (classDefinition != null)
|
|
{
|
|
boolean wasMLAware = MLPropertyInterceptor.setMLAware(true);
|
|
try
|
|
{
|
|
// Copy the properties
|
|
Map<QName,PropertyDefinition> propertyDefinitions = classDefinition.getProperties();
|
|
for (QName propertyName : propertyDefinitions.keySet())
|
|
{
|
|
Serializable propValue = this.nodeService.getProperty(nodeRef, propertyName);
|
|
nodeDetails.addProperty(classRef, propertyName, propValue);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
MLPropertyInterceptor.setMLAware(wasMLAware);
|
|
}
|
|
|
|
// Version the associations (child and target)
|
|
Map<QName, AssociationDefinition> assocDefs = classDefinition.getAssociations();
|
|
|
|
// TODO: Need way of getting child assocs of a given type
|
|
if (classDefinition.isContainer())
|
|
{
|
|
List<ChildAssociationRef> childAssocRefs = this.nodeService.getChildAssocs(nodeRef);
|
|
for (ChildAssociationRef childAssocRef : childAssocRefs)
|
|
{
|
|
if (assocDefs.containsKey(childAssocRef.getTypeQName()))
|
|
{
|
|
nodeDetails.addChildAssociation(classDefinition.getName(), childAssocRef);
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Need way of getting assocs of a given type
|
|
List<AssociationRef> nodeAssocRefs = this.nodeService.getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL);
|
|
for (AssociationRef nodeAssocRef : nodeAssocRefs)
|
|
{
|
|
if (assocDefs.containsKey(nodeAssocRef.getTypeQName()))
|
|
{
|
|
nodeDetails.addAssociation(classDefinition.getName(), nodeAssocRef);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isAVersion(NodeRef nodeRef)
|
|
{
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
@Override
|
|
public boolean isVersioned(NodeRef nodeRef)
|
|
{
|
|
return this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE);
|
|
}
|
|
}
|