getVersionAssocs(NodeRef versionHistoryRef, boolean preLoad)
{
// note: resultant list is ordered by (a) explicit index and (b) association creation time
return dbNodeService.getChildAssocs(versionHistoryRef, Version2Model.CHILD_QNAME_VERSIONS, RegexQNamePattern.MATCH_ALL, preLoad);
}
/**
* 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 of versions with current one last and root one first.
List versions = getAllVersions(versionHistoryRef);
if (versionComparatorDesc != null)
{
Collections.sort(versions, Collections.reverseOrder(versionComparatorDesc));
}
// Build the version history object
boolean isRoot = true;
Version preceeding = null;
for (Version version : versions)
{
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 (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=\n" + 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))
{
// deprecated (unused)
//versionProperties.put(VersionBaseModel.PROP_VERSION_NUMBER, (Integer)value);
}
else
{
if (keyName.equals(Version.PROP_DESCRIPTION) ||
keyName.equals(VersionBaseModel.PROP_VERSION_LABEL) ||
keyName.equals(VersionBaseModel.PROP_VERSION_NUMBER))
{
// ignore reserved localname (including cm:description, cm:versionLabel)
}
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);
if (logger.isTraceEnabled())
{
logger.trace("getVersion: " + versionRef + " versionProperties=\n" + versionProperties.keySet());
}
// 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);
}
// assume noderef is a 'live' node
NodeRef vhNodeRef = this.dbNodeService.getChildByName(getRootNode(), Version2Model.CHILD_QNAME_VERSION_HISTORIES, nodeRef.getId());
// DEPRECATED: for backwards compatibility, in case of a version node (eg. via getCurrentVersion) can lookup 'live' node via UUID
if (vhNodeRef == null)
{
if (nodeService.exists(nodeRef))
{
vhNodeRef = this.dbNodeService.getChildByName(getRootNode(), Version2Model.CHILD_QNAME_VERSION_HISTORIES, (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_UUID));
}
}
return vhNodeRef;
}
/**
* Gets current version of the passed node ref
*
* This uses the version label as a mechanism for looking up the version node.
*/
private Pair getCurrentVersionImpl(NodeRef versionHistoryRef, NodeRef nodeRef)
{
Pair result = null;
String versionLabel = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL);
// note: resultant list is ordered by (a) explicit index and (b) association creation time
List versionAssocs = getVersionAssocs(versionHistoryRef, false);
// Current version should be head version (since no branching)
int cnt = versionAssocs.size();
for (int i = cnt; i > 0; i--)
{
ChildAssociationRef versionAssoc = versionAssocs.get(i-1);
String tempLabel = (String)this.dbNodeService.getProperty(versionAssoc.getChildRef(), Version2Model.PROP_QNAME_VERSION_LABEL);
if (tempLabel != null && tempLabel.equals(versionLabel) == true)
{
boolean headVersion = (i == cnt);
if (! headVersion)
{
if (logger.isDebugEnabled())
{
logger.debug("Unexpected: current version does not appear to be 1st version in the list ["+versionHistoryRef+", "+nodeRef+"]");
}
}
result = new Pair(headVersion, getVersion(versionAssoc.getChildRef()));
break;
}
}
return result;
}
/**
* Check if versions are marked with invalid version label, if true > apply default serial version label (e.g. "1.0", "1.1")
*
* @param versionHistory a version histore node reference
* @param nodeRef a node reference
*/
private void checkForCorruptedVersions(NodeRef versionHistory, NodeRef nodeRef)
{
// get the current version label in live store
String versionLabel = (String) this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL);
if (versionLabel != null && versionLabel.equals("0"))
{
// need to correct version labels
List versions = getAllVersions(versionHistory);
// sort versions by node id
Collections.sort(versions, new Comparator()
{
public int compare(Version v1, Version v2)
{
int result = v1.getFrozenModifiedDate().compareTo(v2.getFrozenModifiedDate());
if (result == 0)
{
Long dbid1 = (Long)nodeService.getProperty(v1.getFrozenStateNodeRef(), ContentModel.PROP_NODE_DBID);
Long dbid2 = (Long)nodeService.getProperty(v2.getFrozenStateNodeRef(), ContentModel.PROP_NODE_DBID);
if (dbid1 != null && dbid2 != null)
{
result = dbid1.compareTo(dbid2);
}
else
{
result = 0;
if (logger.isWarnEnabled())
{
logger.warn("node-dbid property is missing for versions: " + v1.toString() + " or " + v2.toString());
}
}
}
return result;
}
});
SerialVersionLabelPolicy serialVersionLabelPolicy = new SerialVersionLabelPolicy();
QName classRef = this.nodeService.getType(nodeRef);
Version preceedingVersion = null;
for (Version version : versions)
{
// re-calculate version label
versionLabel = serialVersionLabelPolicy.calculateVersionLabel(classRef, preceedingVersion, 0, version.getVersionProperties());
// update version with new version label
NodeRef versionNodeRef = new NodeRef(StoreRef.PROTOCOL_WORKSPACE, version.getFrozenStateNodeRef().getStoreRef().getIdentifier(), version.getFrozenStateNodeRef()
.getId());
this.dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_VERSION_LABEL, versionLabel);
version.getVersionProperties().put(VersionBaseModel.PROP_VERSION_LABEL, versionLabel);
// remember preceding version
preceedingVersion = version;
}
// update current version label in live store
this.nodeService.setProperty(nodeRef, ContentModel.PROP_VERSION_LABEL, versionLabel);
}
}
/**
* @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));
List versionedChildren = this.nodeService.getChildAssocs(versionNodeRef);
for (ChildAssociationRef versionedChild : versionedChildren)
{
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. Despite 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
String childRefName = (String) this.nodeService.getProperty(versionedChild.getChildRef(), ContentModel.PROP_NAME);
NodeRef childAssocOnVersionNode = this.nodeService.getChildByName(nodeRef, versionedChild.getTypeQName(), childRefName);
if (childAssocOnVersionNode == null )
{
this.nodeService.addChild(nodeRef, versionedChild.getChildRef(), versionedChild.getTypeQName(), versionedChild.getQName());
}
}
else
{
if (versionedChild.isPrimary() == true)
{
// Only try to restore 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 target 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);
}
}
}
/* (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());
props.put(ContentModel.PROP_VERSION_LABEL, version.getVersionLabel());
// 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)
{
NodeRef versionHistoryNodeRef = getVersionHistoryNodeRef(nodeRef);
Version headVersion = null;
if (versionHistoryNodeRef != null)
{
VersionHistory versionHistory = buildVersionHistory(versionHistoryNodeRef, nodeRef);
if (versionHistory != null)
{
headVersion = versionHistory.getHeadVersion();
}
}
return headVersion;
}
/* (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)
{
try
{
// Disable auto-version behaviour
this.policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_VERSIONABLE);
// 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);
}
}
finally
{
this.policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_VERSIONABLE);
}
}
}
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.version.VersionService#deleteVersion(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.version.Version)
*/
public void deleteVersion(NodeRef nodeRef, Version version)
{
if (useDeprecatedV1)
{
super.deleteVersion(nodeRef, version); // throws UnsupportedOperationException
}
else
{
// Check the mandatory parameters
ParameterCheck.mandatory("nodeRef", nodeRef);
ParameterCheck.mandatory("version", version);
Version currentVersion = getCurrentVersion(nodeRef);
// Delete the version node
this.dbNodeService.deleteNode(VersionUtil.convertNodeRef(version.getFrozenStateNodeRef()));
if (currentVersion.getVersionLabel().equals(version.getVersionLabel()))
{
Version headVersion = getHeadVersion(nodeRef);
if (headVersion != null)
{
// Reset the version label property on the versionable node to new head version
// Disable the VersionableAspect for this change though, we don't want
// to have this create a new version for the property change!
policyBehaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE);
this.nodeService.setProperty(nodeRef, ContentModel.PROP_VERSION_LABEL, headVersion.getVersionLabel());
}
else
{
deleteVersionHistory(nodeRef);
}
}
}
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.version.VersionService#isAVersion(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public boolean isAVersion(NodeRef nodeRef)
{
NodeRef realNodeRef = nodeRef;
if(nodeRef.getStoreRef().getProtocol().equals(VersionBaseModel.STORE_PROTOCOL))
{
realNodeRef = VersionUtil.convertNodeRef(nodeRef);
}
return this.dbNodeService.hasAspect(realNodeRef, Version2Model.ASPECT_VERSION);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.version.VersionService#isVersioned(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public boolean isVersioned(NodeRef nodeRef)
{
NodeRef realNodeRef = nodeRef;
if(nodeRef.getStoreRef().getProtocol().equals(VersionBaseModel.STORE_PROTOCOL))
{
realNodeRef = VersionUtil.convertNodeRef(nodeRef);
}
return this.dbNodeService.hasAspect(realNodeRef, ContentModel.ASPECT_VERSIONABLE);
}
}