();
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.
*
* 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;
ArrayList versionHistoryNodeRefs = new ArrayList();
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 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);
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 versionProperties = new HashMap();
// Get the standard node details
Map 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 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 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 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 versionAspectProperties =
versionable.getProperties().keySet();
Map props = new HashMap();
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.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 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 origional)
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());
}
// 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 props = new HashMap(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 versionsAssoc = this.dbNodeService.getChildAssocs(versionHistoryNodeRef, RegexQNamePattern.MATCH_ALL, VersionModel.CHILD_QNAME_VERSIONS);
for (ChildAssociationRef versionAssoc : versionsAssoc)
{
NodeRef versionNodeRef = versionAssoc.getChildRef();
List 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 versionsAssoc = this.dbNodeService.getChildAssocs(versionHistoryNodeRef, RegexQNamePattern.MATCH_ALL, VersionModel.CHILD_QNAME_VERSIONS);
for (ChildAssociationRef versionAssoc : versionsAssoc)
{
NodeRef versionNodeRef = versionAssoc.getChildRef();
List 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 versionProperties,
PolicyScope nodeDetails)
{
ClassDefinition classDefinition = this.dictionaryService.getClass(classRef);
if (classDefinition != null)
{
boolean wasMLAware = MLPropertyInterceptor.setMLAware(true);
try
{
// Copy the properties
Map 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 assocDefs = classDefinition.getAssociations();
// TODO: Need way of getting child assocs of a given type
if (classDefinition.isContainer())
{
List 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 nodeAssocRefs = this.nodeService.getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL);
for (AssociationRef nodeAssocRef : nodeAssocRefs)
{
if (assocDefs.containsKey(nodeAssocRef.getTypeQName()))
{
nodeDetails.addAssociation(classDefinition.getName(), nodeAssocRef);
}
}
}
}
}