KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > alfresco > repo > version > VersionServiceImpl


1 /*
2  * Copyright (C) 2005 Alfresco, Inc.
3  *
4  * Licensed under the Mozilla Public License version 1.1
5  * with a permitted attribution clause. You may obtain a
6  * copy of the License at
7  *
8  * http://www.alfresco.org/legal/license.txt
9  *
10  * Unless required by applicable law or agreed to in writing,
11  * software distributed under the License is distributed on an
12  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13  * either express or implied. See the License for the specific
14  * language governing permissions and limitations under the
15  * License.
16  */

17 package org.alfresco.repo.version;
18
19 import java.io.Serializable JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.HashSet JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.Map JavaDoc;
26 import java.util.Set JavaDoc;
27
28 import org.alfresco.model.ContentModel;
29 import org.alfresco.repo.policy.BehaviourFilter;
30 import org.alfresco.repo.policy.JavaBehaviour;
31 import org.alfresco.repo.policy.PolicyScope;
32 import org.alfresco.repo.version.common.AbstractVersionServiceImpl;
33 import org.alfresco.repo.version.common.VersionHistoryImpl;
34 import org.alfresco.repo.version.common.VersionImpl;
35 import org.alfresco.repo.version.common.VersionUtil;
36 import org.alfresco.repo.version.common.counter.VersionCounterDaoService;
37 import org.alfresco.repo.version.common.versionlabel.SerialVersionLabelPolicy;
38 import org.alfresco.service.cmr.repository.AspectMissingException;
39 import org.alfresco.service.cmr.repository.AssociationRef;
40 import org.alfresco.service.cmr.repository.ChildAssociationRef;
41 import org.alfresco.service.cmr.repository.NodeRef;
42 import org.alfresco.service.cmr.repository.NodeService;
43 import org.alfresco.service.cmr.repository.StoreRef;
44 import org.alfresco.service.cmr.search.SearchService;
45 import org.alfresco.service.cmr.version.ReservedVersionNameException;
46 import org.alfresco.service.cmr.version.Version;
47 import org.alfresco.service.cmr.version.VersionHistory;
48 import org.alfresco.service.cmr.version.VersionService;
49 import org.alfresco.service.cmr.version.VersionServiceException;
50 import org.alfresco.service.namespace.NamespaceService;
51 import org.alfresco.service.namespace.QName;
52 import org.alfresco.service.namespace.RegexQNamePattern;
53 import org.alfresco.util.ParameterCheck;
54
55 /**
56  * The version service implementation.
57  *
58  * @author Roy Wetheral
59  */

60 public class VersionServiceImpl extends AbstractVersionServiceImpl
61                                 implements VersionService, VersionModel
62 {
63     /**
64      * Error message I18N id's
65      */

66     private static final String JavaDoc MSGID_ERR_NOT_FOUND = "version_service.err_not_found";
67     private static final String JavaDoc MSGID_ERR_NO_BRANCHES = "version_service.err_unsupported";
68     private static final String JavaDoc MSGID_ERR_RESTORE_EXISTS = "version_service.err_restore_exists";
69     private static final String JavaDoc MSGID_ERR_ONE_PRECEEDING = "version_service.err_one_preceeding";
70     private static final String JavaDoc MSGID_ERR_RESTORE_NO_VERSION = "version_service.err_restore_no_version";
71     private static final String JavaDoc MSGID_ERR_REVERT_MISMATCH = "version_service.err_revert_mismatch";
72     
73     /**
74      * The version counter service
75      */

76     private VersionCounterDaoService versionCounterService ;
77     
78     /**
79      * The db node service, used as the version store implementation
80      */

81     protected NodeService dbNodeService;
82     
83     /**
84      * Policy behaviour filter
85      */

86     private BehaviourFilter policyBehaviourFilter;
87
88     /**
89      * The repository searcher
90      */

91     @SuppressWarnings JavaDoc("unused")
92     private SearchService searcher;
93     
94     /**
95      * The version cache
96      */

97     private HashMap JavaDoc<NodeRef, Version> versionCache = new HashMap JavaDoc<NodeRef, Version>(100);
98     
99     /**
100      * Sets the db node service, used as the version store implementation
101      *
102      * @param nodeService the node service
103      */

104     public void setDbNodeService(NodeService nodeService)
105     {
106         this.dbNodeService = nodeService;
107     }
108
109     /**
110      * Sets the searcher
111      *
112      * @param searcher the searcher
113      */

114     public void setSearcher(SearchService searcher)
115     {
116         this.searcher = searcher;
117     }
118     
119     /**
120      * Sets the version counter service
121      *
122      * @param versionCounterService the version counter service
123      */

124     public void setVersionCounterDaoService(VersionCounterDaoService versionCounterService)
125     {
126         this.versionCounterService = versionCounterService;
127     }
128     
129     /**
130      * Set the policy behaviour filter
131      *
132      * @param policyBehaviourFilter the policy behaviour filter
133      */

134     public void setPolicyBehaviourFilter(BehaviourFilter policyBehaviourFilter)
135     {
136         this.policyBehaviourFilter = policyBehaviourFilter;
137     }
138     
139     /**
140      * Initialise method
141      */

142     @Override JavaDoc
143     public void initialise()
144     {
145         super.initialise();
146         
147         // Register the serial version label behaviour
148
this.policyComponent.bindClassBehaviour(
149                 QName.createQName(NamespaceService.ALFRESCO_URI, "calculateVersionLabel"),
150                 ContentModel.TYPE_CMOBJECT,
151                 new JavaBehaviour(new SerialVersionLabelPolicy(), "calculateVersionLabel"));
152     }
153     
154     /**
155      * Gets the reference to the version store
156      *
157      * @return reference to the version store
158      */

159     public StoreRef getVersionStoreReference()
160     {
161         return new StoreRef(
162                 StoreRef.PROTOCOL_WORKSPACE,
163                 VersionModel.STORE_ID);
164     }
165     
166     /**
167      * @see VersionCounterDaoService#nextVersionNumber(StoreRef)
168      */

169     public Version createVersion(
170             NodeRef nodeRef,
171             Map JavaDoc<String JavaDoc, Serializable JavaDoc> versionProperties)
172             throws ReservedVersionNameException, AspectMissingException
173     {
174         // Get the next version number
175
int versionNumber = this.versionCounterService.nextVersionNumber(getVersionStoreReference());
176         
177         // Create the version
178
return createVersion(nodeRef, versionProperties, versionNumber);
179     }
180
181     /**
182      * The version's are created from the children upwards with the parent being created first. This will
183      * ensure that the child version references in the version node will point to the version history nodes
184      * for the (possibly) newly created version histories.
185      */

186     public Collection JavaDoc<Version> createVersion(
187             NodeRef nodeRef,
188             Map JavaDoc<String JavaDoc, Serializable JavaDoc> versionProperties,
189             boolean versionChildren)
190             throws ReservedVersionNameException, AspectMissingException
191     {
192         // Get the next version number
193
int versionNumber = this.versionCounterService.nextVersionNumber(getVersionStoreReference());
194         
195         // Create the versions
196
return createVersion(nodeRef, versionProperties, versionChildren, versionNumber);
197     }
198     
199     /**
200      * Helper method used to create the version when the versionChildren flag is provided. This method
201      * ensures that all the children (if the falg is set to true) are created with the same version
202      * number, this ensuring that the version stripe is correct.
203      *
204      * @param nodeRef the parent node reference
205      * @param versionProperties the version properties
206      * @param versionChildren indicates whether to version the children of the parent
207      * node
208      * @param versionNumber the version number
209      
210      * @return a collection of the created versions
211      * @throws ReservedVersionNameException thrown if there is a reserved version property name clash
212      * @throws AspectMissingException thrown if the version aspect is missing from a node
213      */

214     private Collection JavaDoc<Version> createVersion(
215             NodeRef nodeRef,
216             Map JavaDoc<String JavaDoc, Serializable JavaDoc> versionProperties,
217             boolean versionChildren,
218             int versionNumber)
219             throws ReservedVersionNameException, AspectMissingException
220     {
221
222         Collection JavaDoc<Version> result = new ArrayList JavaDoc<Version>();
223         
224         if (versionChildren == true)
225         {
226             // Get the children of the node
227
Collection JavaDoc<ChildAssociationRef> children = this.dbNodeService.getChildAssocs(nodeRef);
228             for (ChildAssociationRef childAssoc : children)
229             {
230                 // Recurse into this method to version all the children with the same version number
231
Collection JavaDoc<Version> childVersions = createVersion(
232                         childAssoc.getChildRef(),
233                         versionProperties,
234                         versionChildren,
235                         versionNumber);
236                 result.addAll(childVersions);
237             }
238         }
239         
240         result.add(createVersion(nodeRef, versionProperties, versionNumber));
241         
242         return result;
243     }
244
245     /**
246      * Note: we can't control the order of the list, so if we have children and parents in the list and the
247      * parents get versioned before the children and the children are not already versioned then the parents
248      * child references will be pointing to the node ref, rather than the verison history.
249      */

250     public Collection JavaDoc<Version> createVersion(
251             Collection JavaDoc<NodeRef> nodeRefs,
252             Map JavaDoc<String JavaDoc, Serializable JavaDoc> versionProperties)
253             throws ReservedVersionNameException, AspectMissingException
254     {
255         Collection JavaDoc<Version> result = new ArrayList JavaDoc<Version>(nodeRefs.size());
256         
257         // Get the next version number
258
int versionNumber = this.versionCounterService.nextVersionNumber(getVersionStoreReference());
259         
260         // Version each node in the list
261
for (NodeRef nodeRef : nodeRefs)
262         {
263             result.add(createVersion(nodeRef, versionProperties, versionNumber));
264         }
265         
266         return result;
267     }
268     
269     /**
270      * Creates a new version of the passed node assigning the version properties
271      * accordingly.
272      *
273      * @param nodeRef a node reference
274      * @param versionProperties the version properties
275      * @param versionNumber the version number
276      * @return the newly created version
277      * @throws ReservedVersionNameException
278      * thrown if there is a name clash in the version properties
279      */

280     private Version createVersion(
281             NodeRef nodeRef,
282             Map JavaDoc<String JavaDoc, Serializable JavaDoc> origVersionProperties,
283             int versionNumber)
284             throws ReservedVersionNameException
285     {
286
287         // Copy the version properties (to prevent unexpected side effects to the caller)
288
Map JavaDoc<String JavaDoc, Serializable JavaDoc> versionProperties = new HashMap JavaDoc<String JavaDoc, Serializable JavaDoc>();
289         if (origVersionProperties != null)
290         {
291             versionProperties.putAll(origVersionProperties);
292         }
293         
294         // If the version aspect is not there then add it
295
if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == false)
296         {
297             this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, null);
298         }
299         
300         // Call the policy behaviour
301
invokeBeforeCreateVersion(nodeRef);
302         
303         // Check that the supplied additional version properties do not clash with the reserved ones
304
VersionUtil.checkVersionPropertyNames(versionProperties.keySet());
305         
306         // Check the repository for the version history for this node
307
NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef);
308         NodeRef currentVersionRef = null;
309         
310         if (versionHistoryRef == null)
311         {
312             HashMap JavaDoc<QName, Serializable JavaDoc> props = new HashMap JavaDoc<QName, Serializable JavaDoc>();
313             props.put(PROP_QNAME_VERSIONED_NODE_ID, nodeRef.getId());
314             
315             // Create a new version history node
316
ChildAssociationRef childAssocRef = this.dbNodeService.createNode(
317                     getRootNode(),
318                     ContentModel.ASSOC_CHILDREN,
319                     CHILD_QNAME_VERSION_HISTORIES,
320                     TYPE_QNAME_VERSION_HISTORY,
321                     props);
322             versionHistoryRef = childAssocRef.getChildRef();
323         }
324         else
325         {
326             // Since we have an exisiting version history we should be able to lookup
327
// the current version
328
currentVersionRef = getCurrentVersionNodeRef(versionHistoryRef, nodeRef);
329             
330             if (currentVersionRef == null)
331             {
332                 throw new VersionServiceException(MSGID_ERR_NOT_FOUND);
333             }
334             
335             // Need to check that we are not about to create branch since this is not currently supported
336
VersionHistory versionHistory = buildVersionHistory(versionHistoryRef, nodeRef);
337             Version currentVersion = getVersion(currentVersionRef);
338             if (versionHistory.getSuccessors(currentVersion).size() != 0)
339             {
340                 throw new VersionServiceException(MSGID_ERR_NO_BRANCHES);
341             }
342         }
343         
344         // Create the node details
345
QName classRef = this.nodeService.getType(nodeRef);
346         PolicyScope nodeDetails = new PolicyScope(classRef);
347         
348         // Get the node details by calling the onVersionCreate policy behaviour
349
invokeOnCreateVersion(nodeRef, versionProperties, nodeDetails);
350         
351         // Create the new version node (child of the version history)
352
NodeRef newVersionRef = createNewVersion(
353                 nodeRef,
354                 versionHistoryRef,
355                 getStandardVersionProperties(versionProperties, nodeRef, currentVersionRef, versionNumber),
356                 versionProperties,
357                 nodeDetails);
358         
359         if (currentVersionRef == null)
360         {
361             // Set the new version to be the root version in the version history
362
this.dbNodeService.createAssociation(
363                     versionHistoryRef,
364                     newVersionRef,
365                     VersionServiceImpl.ASSOC_ROOT_VERSION);
366         }
367         else
368         {
369             // Relate the new version to the current version as its successor
370
this.dbNodeService.createAssociation(
371                     currentVersionRef,
372                     newVersionRef,
373                     VersionServiceImpl.ASSOC_SUCCESSOR);
374         }
375         
376         // Create the version data object
377
Version version = getVersion(newVersionRef);
378         
379         // Set the new version label on the versioned node
380
this.nodeService.setProperty(
381                 nodeRef,
382                 ContentModel.PROP_VERSION_LABEL,
383                 version.getVersionLabel());
384         
385         // Return the data object representing the newly created version
386
return version;
387     }
388
389     /**
390      * @see org.alfresco.service.cmr.version.VersionService#getVersionHistory(NodeRef)
391      */

392     public VersionHistory getVersionHistory(NodeRef nodeRef)
393     {
394         VersionHistory versionHistory = null;
395         
396         if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true)
397         {
398             NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef);
399             if (versionHistoryRef != null)
400             {
401                 versionHistory = buildVersionHistory(versionHistoryRef, nodeRef);
402             }
403         }
404             
405         return versionHistory;
406     }
407     
408     /**
409      * @see VersionService#getCurrentVersion(NodeRef)
410      */

411     public Version getCurrentVersion(NodeRef nodeRef)
412     {
413         Version version = null;
414         
415         if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true)
416         {
417             VersionHistory versionHistory = getVersionHistory(nodeRef);
418             if (versionHistory != null)
419             {
420                 String JavaDoc versionLabel = (String JavaDoc)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL);
421                 version = versionHistory.getVersion(versionLabel);
422             }
423         }
424         
425         return version;
426     }
427     
428     /**
429      * Get a map containing the standard list of version properties populated.
430      *
431      * @param versionProperties the version meta data properties
432      * @param nodeRef the node reference
433      * @param preceedingNodeRef the preceeding node reference
434      * @param versionNumber the version number
435      * @return the standard version properties
436      */

437     private Map JavaDoc<QName, Serializable JavaDoc> getStandardVersionProperties(Map JavaDoc<String JavaDoc, Serializable JavaDoc> versionProperties, NodeRef nodeRef, NodeRef preceedingNodeRef, int versionNumber)
438     {
439         Map JavaDoc<QName, Serializable JavaDoc> result = new HashMap JavaDoc<QName, Serializable JavaDoc>(10);
440         
441         // Set the version number for the new version
442
result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_VERSION_NUMBER), Integer.toString(versionNumber));
443         
444         // Set the versionable node id
445
result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_ID), nodeRef.getId());
446         
447         // Set the versionable node store protocol
448
result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_PROTOCOL), nodeRef.getStoreRef().getProtocol());
449         
450         // Set the versionable node store id
451
result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_ID), nodeRef.getStoreRef().getIdentifier());
452         
453         // Store the current node type
454
QName nodeType = this.nodeService.getType(nodeRef);
455         result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_TYPE), nodeType);
456         
457         // Store the current aspects
458
Set JavaDoc<QName> aspects = this.nodeService.getAspects(nodeRef);
459         result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_ASPECTS), (Serializable JavaDoc)aspects);
460         
461         // Calculate the version label
462
QName classRef = this.nodeService.getType(nodeRef);
463         Version preceedingVersion = getVersion(preceedingNodeRef);
464         String JavaDoc versionLabel = invokeCalculateVersionLabel(classRef, preceedingVersion, versionNumber, versionProperties);
465         result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_VERSION_LABEL), versionLabel);
466         
467         return result;
468     }
469     
470     /**
471      * Creates a new version node, setting the properties both calculated and specified.
472      *
473      * @param versionableNodeRef the reference to the node being versioned
474      * @param versionHistoryRef version history node reference
475      * @param preceedingNodeRef the version node preceeding this in the version history
476      * , null if none
477      * @param versionProperties version properties
478      * @param versionNumber the version number
479      * @return the version node reference
480      */

481     private NodeRef createNewVersion(
482             NodeRef versionableNodeRef,
483             NodeRef versionHistoryRef,
484             Map JavaDoc<QName, Serializable JavaDoc> standardVersionProperties,
485             Map JavaDoc<String JavaDoc, Serializable JavaDoc> versionProperties,
486             PolicyScope nodeDetails)
487     {
488         // Create the new version
489
ChildAssociationRef childAssocRef = this.dbNodeService.createNode(
490                 versionHistoryRef,
491                 CHILD_QNAME_VERSIONS,
492                 CHILD_QNAME_VERSIONS,
493                 TYPE_QNAME_VERSION,
494                 standardVersionProperties);
495         NodeRef versionNodeRef = childAssocRef.getChildRef();
496         
497         // Store the meta data
498
storeVersionMetaData(versionNodeRef, versionProperties);
499         
500         // Freeze the various parts of the node
501
freezeProperties(versionNodeRef, nodeDetails.getProperties());
502         freezeChildAssociations(versionNodeRef, nodeDetails.getChildAssociations());
503         freezeAssociations(versionNodeRef, nodeDetails.getAssociations());
504         freezeAspects(nodeDetails, versionNodeRef, nodeDetails.getAspects());
505         
506         // Return the created node reference
507
return versionNodeRef;
508     }
509     
510     /**
511      * Store the version meta data
512      *
513      * @param versionNodeRef the version node reference
514      * @param versionProperties the version properties
515      */

516     private void storeVersionMetaData(NodeRef versionNodeRef, Map JavaDoc<String JavaDoc, Serializable JavaDoc> versionProperties)
517     {
518         for (Map.Entry JavaDoc<String JavaDoc, Serializable JavaDoc> entry : versionProperties.entrySet())
519         {
520             HashMap JavaDoc<QName, Serializable JavaDoc> properties = new HashMap JavaDoc<QName, Serializable JavaDoc>();
521             
522             properties.put(PROP_QNAME_META_DATA_NAME, entry.getKey());
523             properties.put(PROP_QNAME_META_DATA_VALUE, entry.getValue());
524             
525             this.dbNodeService.createNode(
526                     versionNodeRef,
527                     CHILD_QNAME_VERSION_META_DATA,
528                     CHILD_QNAME_VERSION_META_DATA,
529                     TYPE_QNAME_VERSION_META_DATA_VALUE,
530                     properties);
531         }
532     }
533
534     /**
535      * Freeze the aspects
536      *
537      * @param nodeDetails the node details
538      * @param versionNodeRef the version node reference
539      * @param aspects the set of aspects
540      */

541     private void freezeAspects(PolicyScope nodeDetails, NodeRef versionNodeRef, Set JavaDoc<QName> aspects)
542     {
543         for (QName aspect : aspects)
544         {
545             // Freeze the details of the aspect
546
freezeProperties(versionNodeRef, nodeDetails.getProperties(aspect));
547             freezeChildAssociations(versionNodeRef, nodeDetails.getChildAssociations(aspect));
548             freezeAssociations(versionNodeRef, nodeDetails.getAssociations(aspect));
549         }
550     }
551
552     /**
553      * Freeze associations
554      *
555      * @param versionNodeRef the version node reference
556      * @param associations the list of associations
557      */

558     private void freezeAssociations(NodeRef versionNodeRef, List JavaDoc<AssociationRef> associations)
559     {
560         for (AssociationRef targetAssoc : associations)
561         {
562             HashMap JavaDoc<QName, Serializable JavaDoc> properties = new HashMap JavaDoc<QName, Serializable JavaDoc>();
563             
564             // Set the qname of the association
565
properties.put(PROP_QNAME_ASSOC_TYPE_QNAME, targetAssoc.getTypeQName());
566             
567             // Set the reference property to point to the child node
568
properties.put(ContentModel.PROP_REFERENCE, targetAssoc.getTargetRef());
569             
570             // Create child version reference
571
this.dbNodeService.createNode(
572                     versionNodeRef,
573                     CHILD_QNAME_VERSIONED_ASSOCS,
574                     CHILD_QNAME_VERSIONED_ASSOCS,
575                     TYPE_QNAME_VERSIONED_ASSOC,
576                     properties);
577         }
578     }
579
580     /**
581      * Freeze child associations
582      *
583      * @param versionNodeRef the version node reference
584      * @param childAssociations the child associations
585      */

586     private void freezeChildAssociations(NodeRef versionNodeRef, List JavaDoc<ChildAssociationRef> childAssociations)
587     {
588         for (ChildAssociationRef childAssocRef : childAssociations)
589         {
590             HashMap JavaDoc<QName, Serializable JavaDoc> properties = new HashMap JavaDoc<QName, Serializable JavaDoc>();
591             
592             // Set the qname, isPrimary and nthSibling properties
593
properties.put(PROP_QNAME_ASSOC_QNAME, childAssocRef.getQName());
594             properties.put(PROP_QNAME_ASSOC_TYPE_QNAME, childAssocRef.getTypeQName());
595             properties.put(PROP_QNAME_IS_PRIMARY, Boolean.valueOf(childAssocRef.isPrimary()));
596             properties.put(PROP_QNAME_NTH_SIBLING, Integer.valueOf(childAssocRef.getNthSibling()));
597             
598             // Set the reference property to point to the child node
599
properties.put(ContentModel.PROP_REFERENCE, childAssocRef.getChildRef());
600             
601             // Create child version reference
602
this.dbNodeService.createNode(
603                     versionNodeRef,
604                     CHILD_QNAME_VERSIONED_CHILD_ASSOCS,
605                     CHILD_QNAME_VERSIONED_CHILD_ASSOCS,
606                     TYPE_QNAME_VERSIONED_CHILD_ASSOC,
607                     properties);
608         }
609     }
610
611     /**
612      * Freeze properties
613      *
614      * @param versionNodeRef the version node reference
615      * @param properties the properties
616      */

617     private void freezeProperties(NodeRef versionNodeRef, Map JavaDoc<QName, Serializable JavaDoc> properties)
618     {
619         // Copy the property values from the node onto the version node
620
for (Map.Entry JavaDoc<QName, Serializable JavaDoc> entry : properties.entrySet())
621         {
622             // Get the property values
623
HashMap JavaDoc<QName, Serializable JavaDoc> props = new HashMap JavaDoc<QName, Serializable JavaDoc>();
624             props.put(PROP_QNAME_QNAME, entry.getKey());
625             
626             if (entry.getValue() instanceof Collection JavaDoc)
627             {
628                 props.put(PROP_QNAME_MULTI_VALUE, entry.getValue());
629                 props.put(PROP_QNAME_IS_MULTI_VALUE, true);
630             }
631             else
632             {
633                 props.put(PROP_QNAME_VALUE, entry.getValue());
634                 props.put(PROP_QNAME_IS_MULTI_VALUE, false);
635             }
636             
637             // Create the node storing the frozen attribute details
638
this.dbNodeService.createNode(
639                     versionNodeRef,
640                     CHILD_QNAME_VERSIONED_ATTRIBUTES,
641                     CHILD_QNAME_VERSIONED_ATTRIBUTES,
642                     TYPE_QNAME_VERSIONED_PROPERTY,
643                     props);
644         }
645     }
646     
647     /**
648      * Gets the version stores root node
649      *
650      * @return the node ref to the root node of the version store
651      */

652     private NodeRef getRootNode()
653     {
654         // Get the version store root node reference
655
return this.dbNodeService.getRootNode(getVersionStoreReference());
656     }
657     
658     /**
659      * Builds a version history object from the version history reference.
660      * <p>
661      * The node ref is passed to enable the version history to be scoped to the
662      * appropriate branch in the version history.
663      *
664      * @param versionHistoryRef the node ref for the version history
665      * @param nodeRef the node reference
666      * @return a constructed version history object
667      */

668     private VersionHistory buildVersionHistory(NodeRef versionHistoryRef, NodeRef nodeRef)
669     {
670         VersionHistory versionHistory = null;
671         
672         ArrayList JavaDoc<NodeRef> versionHistoryNodeRefs = new ArrayList JavaDoc<NodeRef>();
673         NodeRef currentVersion = getCurrentVersionNodeRef(versionHistoryRef, nodeRef);
674         
675         while (currentVersion != null)
676         {
677             AssociationRef preceedingVersion = null;
678             
679             versionHistoryNodeRefs.add(0, currentVersion);
680             
681             List JavaDoc<AssociationRef> preceedingVersions = this.dbNodeService.getSourceAssocs(
682                                                                                 currentVersion,
683                                                                                 VersionModel.ASSOC_SUCCESSOR);
684             if (preceedingVersions.size() == 1)
685             {
686                 preceedingVersion = (AssociationRef)preceedingVersions.toArray()[0];
687                 currentVersion = preceedingVersion.getSourceRef();
688             }
689             else if (preceedingVersions.size() > 1)
690             {
691                 // Error since we only currently support one preceeding version
692
throw new VersionServiceException(MSGID_ERR_ONE_PRECEEDING);
693             }
694             else
695             {
696                 currentVersion = null;
697             }
698         }
699         
700         // Build the version history object
701
boolean isRoot = true;
702         Version preceeding = null;
703         for (NodeRef versionRef : versionHistoryNodeRefs)
704         {
705             Version version = getVersion(versionRef);
706             
707             if (isRoot == true)
708             {
709                 versionHistory = new VersionHistoryImpl(version);
710                 isRoot = false;
711             }
712             else
713             {
714                 ((VersionHistoryImpl)versionHistory).addVersion(version, preceeding);
715             }
716             preceeding = version;
717         }
718         
719         return versionHistory;
720     }
721     
722     /**
723      * Constructs the a version object to contain the version information from the version node ref.
724      *
725      * @param versionRef the version reference
726      * @return object containing verison data
727      */

728     private Version getVersion(NodeRef versionRef)
729     {
730         Version result = null;
731         
732         if (versionRef != null)
733         {
734             // check to see if this version is already in the cache
735
result = this.versionCache.get(versionRef);
736             
737             if (result == null)
738             {
739                 Map JavaDoc<String JavaDoc, Serializable JavaDoc> versionProperties = new HashMap JavaDoc<String JavaDoc, Serializable JavaDoc>();
740                 
741                 // Get the standard node details
742
Map JavaDoc<QName, Serializable JavaDoc> nodeProperties = this.dbNodeService.getProperties(versionRef);
743                 for (QName key : nodeProperties.keySet())
744                 {
745                     Serializable JavaDoc value = nodeProperties.get(key);
746                     versionProperties.put(key.getLocalName(), value);
747                 }
748                 
749                 // Get the meta data
750
List JavaDoc<ChildAssociationRef> metaData =
751                     this.dbNodeService.getChildAssocs(versionRef, RegexQNamePattern.MATCH_ALL, CHILD_QNAME_VERSION_META_DATA);
752                 for (ChildAssociationRef ref : metaData)
753                 {
754                     NodeRef metaDataValue = (NodeRef)ref.getChildRef();
755                     String JavaDoc name = (String JavaDoc)this.dbNodeService.getProperty(metaDataValue, PROP_QNAME_META_DATA_NAME);
756                     Serializable JavaDoc value = this.dbNodeService.getProperty(metaDataValue, PROP_QNAME_META_DATA_VALUE);
757                     versionProperties.put(name, value);
758                 }
759                 
760                 // Create and return the version object
761
NodeRef newNodeRef = new NodeRef(new StoreRef(STORE_PROTOCOL, STORE_ID), versionRef.getId());
762                 result = new VersionImpl(versionProperties, newNodeRef);
763                 
764                 // Add the version to the cache
765
this.versionCache.put(versionRef, result);
766             }
767         }
768         
769         return result;
770     }
771     
772     /**
773      * Gets a reference to the version history node for a given 'real' node.
774      *
775      * @param nodeRef a node reference
776      * @return a reference to the version history node, null of none
777      */

778     private NodeRef getVersionHistoryNodeRef(NodeRef nodeRef)
779     {
780         NodeRef result = null;
781         
782         Collection JavaDoc<ChildAssociationRef> versionHistories = this.dbNodeService.getChildAssocs(getRootNode());
783         for (ChildAssociationRef versionHistory : versionHistories)
784         {
785             String JavaDoc nodeId = (String JavaDoc)this.dbNodeService.getProperty(versionHistory.getChildRef(), VersionModel.PROP_QNAME_VERSIONED_NODE_ID);
786             if (nodeId != null && nodeId.equals(nodeRef.getId()) == true)
787             {
788                 result = versionHistory.getChildRef();
789                 break;
790             }
791         }
792         
793         return result;
794     }
795     
796     /**
797      * Gets a reference to the node for the current version of the passed node ref.
798      *
799      * This uses the version label as a mechanism for looking up the version node in
800      * the version history.
801      *
802      * @param nodeRef a node reference
803      * @return a reference to a version reference
804      */

805     private NodeRef getCurrentVersionNodeRef(NodeRef versionHistory, NodeRef nodeRef)
806     {
807         NodeRef result = null;
808         String JavaDoc versionLabel = (String JavaDoc)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL);
809         
810         Collection JavaDoc<ChildAssociationRef> versions = this.dbNodeService.getChildAssocs(versionHistory);
811         for (ChildAssociationRef version : versions)
812         {
813             String JavaDoc tempLabel = (String JavaDoc)this.dbNodeService.getProperty(version.getChildRef(), VersionModel.PROP_QNAME_VERSION_LABEL);
814             if (tempLabel != null && tempLabel.equals(versionLabel) == true)
815             {
816                 result = version.getChildRef();
817                 break;
818             }
819         }
820         
821         return result;
822     }
823     
824     /**
825      * Checks the given node for the version aspect. Throws an exception if it is not present.
826      *
827      * @param nodeRef the node reference
828      * @throws AspectMissingException
829      * the version aspect is not present on the node
830      */

831     private void checkForVersionAspect(NodeRef nodeRef)
832        throws AspectMissingException
833     {
834         QName aspectRef = ContentModel.ASPECT_VERSIONABLE;
835         
836         if (this.nodeService.hasAspect(nodeRef, aspectRef) == false)
837         {
838             // Raise exception to indicate version aspect is not present
839
throw new AspectMissingException(aspectRef, nodeRef);
840         }
841     }
842     
843     /**
844      * @see org.alfresco.cms.version.VersionService#revert(NodeRef)
845      */

846     public void revert(NodeRef nodeRef)
847     {
848         revert(nodeRef, getCurrentVersion(nodeRef), true);
849     }
850     
851     /**
852      * @see org.alfresco.service.cmr.version.VersionService#revert(org.alfresco.service.cmr.repository.NodeRef, boolean)
853      */

854     public void revert(NodeRef nodeRef, boolean deep)
855     {
856         revert(nodeRef, getCurrentVersion(nodeRef), deep);
857     }
858     
859     /**
860      * @see org.alfresco.service.cmr.version.VersionService#revert(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.version.Version)
861      */

862     public void revert(NodeRef nodeRef, Version version)
863     {
864         revert(nodeRef, version, true);
865     }
866
867     /**
868      * @see org.alfresco.service.cmr.version.VersionService#revert(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.version.Version, boolean)
869      */

870     public void revert(NodeRef nodeRef, Version version, boolean deep)
871     {
872         // Check the mandatory parameters
873
ParameterCheck.mandatory("nodeRef", nodeRef);
874         ParameterCheck.mandatory("version", version);
875         
876         // Cross check that the version provided relates to the node reference provided
877
if (nodeRef.getId().equals(version.getVersionProperty(VersionModel.PROP_FROZEN_NODE_ID)) == false)
878         {
879             // Error since the version provided does not correspond to the node reference provided
880
throw new VersionServiceException(MSGID_ERR_REVERT_MISMATCH);
881         }
882         
883         // Turn off any auto-version policy behaviours
884
this.policyBehaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE);
885         try
886         {
887             // Store the current version label
888
String JavaDoc currentVersionLabel = (String JavaDoc)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL);
889             
890             // Get the node that represents the frozen state
891
NodeRef versionNodeRef = version.getFrozenStateNodeRef();
892             
893             // Revert the property values
894
this.nodeService.setProperties(nodeRef, this.nodeService.getProperties(versionNodeRef));
895             
896             // Apply/remove the aspects as required
897
Set JavaDoc<QName> aspects = new HashSet JavaDoc<QName>(this.nodeService.getAspects(nodeRef));
898             for (QName versionAspect : this.nodeService.getAspects(versionNodeRef))
899             {
900                 if (aspects.contains(versionAspect) == false)
901                 {
902                     this.nodeService.addAspect(nodeRef, versionAspect, null);
903                 }
904                 else
905                 {
906                     aspects.remove(versionAspect);
907                 }
908             }
909             for (QName aspect : aspects)
910             {
911                 this.nodeService.removeAspect(nodeRef, aspect);
912             }
913             
914             // Re-add the versionable aspect to the reverted node
915
if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == false)
916             {
917                 this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, null);
918             }
919             
920             // Re-set the version label property (since it should not be modified from the origional)
921
this.nodeService.setProperty(nodeRef, ContentModel.PROP_VERSION_LABEL, currentVersionLabel);
922     
923             // Add/remove the child nodes
924
List JavaDoc<ChildAssociationRef> children = new ArrayList JavaDoc<ChildAssociationRef>(this.nodeService.getChildAssocs(nodeRef));
925             for (ChildAssociationRef versionedChild : this.nodeService.getChildAssocs(versionNodeRef))
926             {
927                 if (children.contains(versionedChild) == false)
928                 {
929                     if (this.nodeService.exists(versionedChild.getChildRef()) == true)
930                     {
931                         // The node was a primary child of the parent, but that is no longer the case. Dispite this
932
// the node still exits so this means it has been moved.
933
// The best thing to do in this situation will be to re-add the node as a child, but it will not
934
// be a primary child.
935
this.nodeService.addChild(nodeRef, versionedChild.getChildRef(), versionedChild.getTypeQName(), versionedChild.getQName());
936                     }
937                     else
938                     {
939                         if (versionedChild.isPrimary() == true)
940                         {
941                             // Only try to resotre missing children if we are doing a deep revert
942
// Look and see if we have a version history for the child node
943
if (deep == true && getVersionHistoryNodeRef(versionedChild.getChildRef()) != null)
944                             {
945                                 // We're going to try and restore the missing child node and recreate the assoc
946
restore(
947                                    versionedChild.getChildRef(),
948                                    nodeRef,
949                                    versionedChild.getTypeQName(),
950                                    versionedChild.getQName());
951                             }
952                             // else the deleted child did not have a version history so we can't restore the child
953
// and so we can't revert the association
954
}
955                         
956                         // else
957
// Since this was never a primary assoc and the child has been deleted we won't recreate
958
// the missing node as it was never owned by the node and we wouldn't know where to put it.
959
}
960                 }
961                 else
962                 {
963                     children.remove(versionedChild);
964                 }
965             }
966             for (ChildAssociationRef ref : children)
967             {
968                 this.nodeService.removeChild(nodeRef, ref.getChildRef());
969             }
970             
971             // Add/remove the target associations
972
for (AssociationRef assocRef : this.nodeService.getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL))
973             {
974                 this.nodeService.removeAssociation(assocRef.getSourceRef(), assocRef.getTargetRef(), assocRef.getTypeQName());
975             }
976             for (AssociationRef versionedAssoc : this.nodeService.getTargetAssocs(versionNodeRef, RegexQNamePattern.MATCH_ALL))
977             {
978                 if (this.nodeService.exists(versionedAssoc.getTargetRef()) == true)
979                 {
980                     this.nodeService.createAssociation(nodeRef, versionedAssoc.getTargetRef(), versionedAssoc.getTypeQName());
981                 }
982                 
983                 // else
984
// Since the tareget of the assoc no longer exists we can't recreate the assoc
985
}
986         }
987         finally
988         {
989             // Turn auto-version policies back on
990
this.policyBehaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE);
991         }
992     }
993     
994     /**
995      * @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)
996      */

997      public NodeRef restore(
998                 NodeRef nodeRef,
999                 NodeRef parentNodeRef,
1000                QName assocTypeQName,
1001                QName assocQName)
1002     {
1003         return restore(nodeRef, parentNodeRef, assocTypeQName, assocQName, true);
1004     }
1005    
1006    /**
1007     * @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)
1008     */

1009     public NodeRef restore(
1010            NodeRef nodeRef,
1011            NodeRef parentNodeRef,
1012            QName assocTypeQName,
1013            QName assocQName,
1014            boolean deep)
1015    {
1016        NodeRef restoredNodeRef = null;
1017             
1018        // Check that the node does not exist
1019
if (this.nodeService.exists(nodeRef) == true)
1020        {
1021            // Error since you can not restore a node that already exists
1022
throw new VersionServiceException(MSGID_ERR_RESTORE_EXISTS, new Object JavaDoc[]{nodeRef.toString()});
1023        }
1024        
1025        // Try and get the version details that we want to restore to
1026
Version version = getHeadVersion(nodeRef);
1027        if (version == null)
1028        {
1029            // Error since there is no version information available to restore the node from
1030
throw new VersionServiceException(MSGID_ERR_RESTORE_NO_VERSION, new Object JavaDoc[]{nodeRef.toString()});
1031        }
1032        
1033        // Set the uuid of the new node
1034
Map JavaDoc<QName, Serializable JavaDoc> props = new HashMap JavaDoc<QName, Serializable JavaDoc>(1);
1035        props.put(ContentModel.PROP_NODE_UUID, version.getVersionProperty(VersionModel.PROP_FROZEN_NODE_ID));
1036        
1037        // Get the type of the node node
1038
QName type = (QName)version.getVersionProperty(VersionModel.PROP_FROZEN_NODE_TYPE);
1039        
1040        // Disable auto-version behaviour
1041
this.policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_VERSIONABLE);
1042        try
1043        {
1044            // Create the restored node
1045
restoredNodeRef = this.nodeService.createNode(
1046                    parentNodeRef,
1047                    assocTypeQName,
1048                    assocQName,
1049                    type,
1050                    props).getChildRef();
1051        }
1052        finally
1053        {
1054            // Enable auto-version behaviour
1055
this.policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_VERSIONABLE);
1056        }
1057        
1058        // Now we need to revert the newly restored node
1059
revert(restoredNodeRef, version, deep);
1060        
1061        return restoredNodeRef;
1062    }
1063    
1064    /**
1065     * Get the head version given a node reference
1066     *
1067     * @param nodeRef the node reference
1068     * @return the 'head' version
1069     */

1070    private Version getHeadVersion(NodeRef nodeRef)
1071    {
1072        Version version = null;
1073        StoreRef storeRef = nodeRef.getStoreRef();
1074        
1075        NodeRef versionHistoryNodeRef = getVersionHistoryNodeRef(nodeRef);
1076        if (versionHistoryNodeRef != null)
1077        {
1078            List JavaDoc<ChildAssociationRef> versionsAssoc = this.dbNodeService.getChildAssocs(versionHistoryNodeRef, RegexQNamePattern.MATCH_ALL, VersionModel.CHILD_QNAME_VERSIONS);
1079            for (ChildAssociationRef versionAssoc : versionsAssoc)
1080            {
1081                NodeRef versionNodeRef = versionAssoc.getChildRef();
1082                List JavaDoc<AssociationRef> successors = this.dbNodeService.getTargetAssocs(versionNodeRef, VersionModel.ASSOC_SUCCESSOR);
1083                if (successors.size() == 0)
1084                {
1085                    String JavaDoc storeProtocol = (String JavaDoc)this.dbNodeService.getProperty(
1086                            versionNodeRef,
1087                            QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_PROTOCOL));
1088                    String JavaDoc storeId = (String JavaDoc)this.dbNodeService.getProperty(
1089                            versionNodeRef,
1090                            QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_ID));
1091                    StoreRef versionStoreRef = new StoreRef(storeProtocol, storeId);
1092                    if (storeRef.equals(versionStoreRef) == true)
1093                    {
1094                        version = getVersion(versionNodeRef);
1095                    }
1096                }
1097            }
1098        }
1099        
1100        return version;
1101    }
1102
1103    /**
1104     * @see org.alfresco.cms.version.VersionService#deleteVersionHistory(NodeRef)
1105     */

1106    public void deleteVersionHistory(NodeRef nodeRef)
1107        throws AspectMissingException
1108    {
1109        // First check that the versionable aspect is present
1110
checkForVersionAspect(nodeRef);
1111        
1112        // Get the version history node for the node is question and delete it
1113
NodeRef versionHistoryNodeRef = getVersionHistoryNodeRef(nodeRef);
1114        this.dbNodeService.deleteNode(versionHistoryNodeRef);
1115        
1116        // Reset the version label property on the versionable node
1117
this.nodeService.setProperty(nodeRef, ContentModel.PROP_VERSION_LABEL, null);
1118    }
1119}
1120
Popular Tags