KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > info > magnolia > cms > core > version > BaseVersionManager


1 /**
2  *
3  * Magnolia and its source-code is licensed under the LGPL.
4  * You may copy, adapt, and redistribute this file for commercial or non-commercial use.
5  * When copying, adapting, or redistributing this document in keeping with the guidelines above,
6  * you are required to provide proper attribution to obinary.
7  * If you reproduce or distribute the document without making any substantive modifications to its content,
8  * please use the following attribution line:
9  *
10  * Copyright 1993-2005 obinary Ltd. (http://www.obinary.com) All rights reserved.
11  *
12  */

13 package info.magnolia.cms.core.version;
14
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17 import org.apache.commons.codec.binary.Base64;
18 import org.apache.commons.io.IOUtils;
19
20 import javax.jcr.*;
21 import javax.jcr.version.Version;
22 import javax.jcr.version.VersionHistory;
23 import javax.jcr.version.VersionIterator;
24 import javax.jcr.version.VersionException;
25
26 import info.magnolia.cms.core.HierarchyManager;
27 import info.magnolia.cms.core.Content;
28 import info.magnolia.cms.core.ItemType;
29 import info.magnolia.cms.core.NodeData;
30 import info.magnolia.cms.util.Rule;
31 import info.magnolia.cms.util.RuleBasedContentFilter;
32 import info.magnolia.cms.util.ExclusiveWrite;
33 import info.magnolia.context.MgnlContext;
34
35 import java.util.List JavaDoc;
36 import java.io.*;
37
38 /**
39  * @author Sameer Charles
40  * $Id$
41  */

42 public abstract class BaseVersionManager {
43
44     /**
45       * version data base
46       */

47      public static final String JavaDoc VERSION_WORKSPACE = "mgnlVersion";
48
49      /**
50       * version workspace system path
51       */

52      protected static final String JavaDoc TMP_REFERENCED_NODES = "mgnl:tmpReferencedNodes";
53
54      /**
55       * version system node, holds this node version specific data
56       */

57      protected static final String JavaDoc SYSTEM_NODE = "mgnl:versionMetaData";
58
59      /**
60       * property name for collection rule
61       */

62      protected static final String JavaDoc PROPERTY_RULE = "Rule";
63
64      /**
65       * jcr root version
66       */

67      protected static final String JavaDoc ROOT_VERSION = "jcr:rootVersion";
68
69      /**
70       * Logger.
71       */

72      private static Logger log = LoggerFactory.getLogger(BaseVersionManager.class);
73
74      /**
75       * create structure needed for version store workspace
76       * @throws RepositoryException if unable to create magnolia system structure
77       */

78      protected void createInitialStructure() throws RepositoryException {
79          HierarchyManager hm = MgnlContext.getSystemContext().getHierarchyManager(VERSION_WORKSPACE);
80          try {
81              Content tmp = hm.getContent("/" + VersionManager.TMP_REFERENCED_NODES);
82              // remove nodes if they are no longer referenced within this workspace
83
NodeIterator children = tmp.getJCRNode().getNodes();
84              while (children.hasNext()) {
85                  Node child = children.nextNode();
86                  if (child.getReferences().getSize() < 1) {
87                      child.remove();
88                  }
89              }
90          }
91          catch (PathNotFoundException e) {
92              hm.createContent("", VersionManager.TMP_REFERENCED_NODES, ItemType.SYSTEM.getSystemName());
93          }
94          hm.save();
95      }
96
97      /**
98       * add version of the specified node and all child nodes while ignoring the same node type
99       * @param node to be versioned
100       * @return newly created version node
101       * @throws UnsupportedOperationException if repository implementation does not support Versions API
102       * @throws javax.jcr.RepositoryException if any repository error occurs
103       */

104      public synchronized Version addVersion(Content node) throws UnsupportedRepositoryOperationException,
105          RepositoryException {
106          // Rule rule = new Rule(new String[] {node.getNodeType().getName(), ItemType.SYSTEM.getSystemName()});
107
Rule rule = new Rule(node.getNodeTypeName() + "," + ItemType.SYSTEM.getSystemName(), ",");
108          rule.reverse();
109          return this.addVersion(node, rule);
110      }
111
112      /**
113       * add version of the specified node and all child nodes while ignoring the same node type
114       * @param node to be versioned
115       * @return newly created version node
116       * @throws UnsupportedOperationException if repository implementation does not support Versions API
117       * @throws javax.jcr.RepositoryException if any repository error occurs
118       */

119      public synchronized Version addVersion(Content node, Rule rule) throws UnsupportedRepositoryOperationException,
120          RepositoryException {
121          List JavaDoc permissions = this.getAccessManagerPermissions();
122          this.impersonateAccessManager(null);
123          try {
124              return this.createVersion(node, rule);
125          }
126          catch (RepositoryException re) {
127              // since add version is synchronized on a singleton object, its safe to revert all changes made in
128
// the session attached to workspace - mgnlVersion
129
log.error("failed to copy versionable node to version store, reverting all changes made in this session");
130              getHierarchyManager().refresh(false);
131              throw re;
132          }
133          finally {
134              this.revertAccessManager(permissions);
135          }
136      }
137
138      /**
139       * create version of the specified node and all child nodes based on the given <code>Rule</code>
140       * @param node to be versioned
141       * @param rule
142       * @return newly created version node
143       * @throws UnsupportedOperationException if repository implementation does not support Versions API
144       * @throws javax.jcr.RepositoryException if any repository error occurs
145       */

146      protected Version createVersion(Content node, Rule rule) throws UnsupportedRepositoryOperationException,
147          RepositoryException {
148          if (isInvalidMaxVersions()) {
149              log.debug("Ignore create version, MaxVersionIndex < 1");
150              log.debug("Returning root version of the source node");
151              return node.getJCRNode().getVersionHistory().getRootVersion();
152          }
153          CopyUtil.getInstance().copyToversion(node, new RuleBasedContentFilter(rule));
154          Content versionedNode = this.getVersionedNode(node);
155          Content systemInfo = this.getSystemNode(versionedNode);
156          // add serialized rule which was used to create this version
157
ByteArrayOutputStream out = new ByteArrayOutputStream();
158          try {
159              ObjectOutput objectOut = new ObjectOutputStream(out);
160              objectOut.writeObject(rule);
161              objectOut.flush();
162              objectOut.close();
163              NodeData nodeData;
164              // PROPERTY_RULE is not a part of MetaData to allow versioning of node types which does NOT support MetaData
165
if (!systemInfo.hasNodeData(PROPERTY_RULE)) {
166                  nodeData = systemInfo.createNodeData(PROPERTY_RULE);
167              }
168              else {
169                  nodeData = systemInfo.getNodeData(PROPERTY_RULE);
170              }
171              nodeData.setValue(new String JavaDoc(Base64.encodeBase64(out.toByteArray())));
172          }
173          catch (IOException e) {
174              throw new RepositoryException("Unable to add serialized Rule to the versioned content");
175          }
176          // temp fix, MgnlContext should always have user either logged-in or anonymous
177
String JavaDoc userName = "";
178          if (MgnlContext.getUser() != null) {
179              userName = MgnlContext.getUser().getName();
180          }
181          // add all system properties for this version
182
if (!systemInfo.hasNodeData(ContentVersion.VERSION_USER)) {
183              systemInfo.createNodeData(ContentVersion.VERSION_USER).setValue(userName);
184          }
185          else {
186              systemInfo.getNodeData(ContentVersion.VERSION_USER).setValue(userName);
187          }
188          if (!systemInfo.hasNodeData(ContentVersion.NAME)) {
189              systemInfo.createNodeData(ContentVersion.NAME).setValue(node.getName());
190          }
191          else {
192              systemInfo.getNodeData(ContentVersion.NAME).setValue(node.getName());
193          }
194
195          versionedNode.save();
196          // add version
197
Version newVersion = versionedNode.getJCRNode().checkin();
198          versionedNode.getJCRNode().checkout();
199          try {
200              this.setMaxVersionHistory(versionedNode);
201          }
202          catch (RepositoryException re) {
203              log.error("Failed to limit version history to the maximum configured", re);
204              log.error("New version has already been created");
205          }
206
207          return newVersion;
208      }
209
210      /**
211       * check if version index is set to negative number
212       * */

213      public abstract boolean isInvalidMaxVersions();
214
215      /**
216       * get node from version store
217       * @param node
218       */

219      public synchronized Content getVersionedNode(Content node) throws RepositoryException {
220          return getVersionedNode(node.getUUID());
221      }
222
223      /**
224       * get node from version store
225       * @param uuid
226       */

227      protected synchronized Content getVersionedNode(String JavaDoc uuid) throws RepositoryException {
228          List JavaDoc permissions = this.getAccessManagerPermissions();
229          this.impersonateAccessManager(null);
230          try {
231              return getHierarchyManager().getContentByUUID(uuid);
232          }
233          catch (ItemNotFoundException e) {
234              return null;
235          }
236          catch (RepositoryException re) {
237              throw re;
238          }
239          finally {
240              this.revertAccessManager(permissions);
241          }
242      }
243
244      /**
245       * set version history to max version possible
246       * @param node
247       * @throws RepositoryException if failed to get VersionHistory or fail to remove
248       */

249      public abstract void setMaxVersionHistory(Content node) throws RepositoryException;
250
251      /**
252       * get history of this node as recorded in the version store
253       * @param node
254       * @return version history of the given node
255       * @throws UnsupportedOperationException if repository implementation does not support Versions API
256       * @throws javax.jcr.RepositoryException if any repository error occurs
257       */

258      public synchronized VersionHistory getVersionHistory(Content node) throws UnsupportedRepositoryOperationException,
259          RepositoryException {
260          Content versionedNode = this.getVersionedNode(node);
261          if (versionedNode == null) {
262              // node does not exist in version store so no version history
263
log.info("No VersionHistory found for this node");
264              return null;
265          }
266          return versionedNode.getJCRNode().getVersionHistory();
267      }
268
269      /**
270       * get named version
271       * @param node
272       * @param name
273       * @return version node
274       * @throws UnsupportedOperationException if repository implementation does not support Versions API
275       * @throws javax.jcr.RepositoryException if any repository error occurs
276       */

277      public synchronized Version getVersion(Content node, String JavaDoc name) throws UnsupportedRepositoryOperationException,
278          RepositoryException {
279          VersionHistory history = this.getVersionHistory(node);
280          if (history != null) {
281              return history.getVersion(name);
282          }
283          log.error("Node " + node.getHandle() + " was never versioned");
284          return null;
285      }
286
287      /**
288       * Returns the current base version of given node
289       * @throws UnsupportedRepositoryOperationException
290       * @throws RepositoryException
291       */

292      public Version getBaseVersion(Content node) throws UnsupportedOperationException JavaDoc, RepositoryException {
293          Content versionedNode = this.getVersionedNode(node);
294          if (versionedNode != null) {
295              return versionedNode.getJCRNode().getBaseVersion();
296          }
297
298          throw new RepositoryException("Node " + node.getHandle() + " was never versioned");
299      }
300
301      /**
302       * get all versions
303       * @param node
304       * @return Version iterator retreived from version history
305       * @throws UnsupportedOperationException if repository implementation does not support Versions API
306       * @throws javax.jcr.RepositoryException if any repository error occurs
307       */

308      public synchronized VersionIterator getAllVersions(Content node) throws UnsupportedRepositoryOperationException,
309          RepositoryException {
310          Content versionedNode = this.getVersionedNode(node);
311          if (versionedNode == null) {
312              // node does not exist in version store so no versions
313
return null;
314          }
315          return versionedNode.getJCRNode().getVersionHistory().getAllVersions();
316      }
317
318      /**
319       * restore specified version
320       * @param node to be restored
321       * @param version to be used
322       * @param removeExisting
323       * @throws javax.jcr.version.VersionException if the specified <code>versionName</code> does not exist in this
324       * node's version history
325       * @throws javax.jcr.RepositoryException if an error occurs
326       * @throws javax.jcr.version.VersionException
327       */

328      public synchronized void restore(Content node, Version version, boolean removeExisting) throws VersionException,
329          UnsupportedRepositoryOperationException, RepositoryException {
330          // get the cloned node from version store
331
Content versionedNode = this.getVersionedNode(node);
332          versionedNode.getJCRNode().restore(version, removeExisting);
333          versionedNode.getJCRNode().checkout();
334          List JavaDoc permissions = this.getAccessManagerPermissions();
335          this.impersonateAccessManager(null);
336          try {
337              // if restored, update original node with the restored node and its subtree
338
Rule rule = this.getUsedFilter(versionedNode);
339              try {
340                  synchronized (ExclusiveWrite.getInstance()) {
341                      CopyUtil.getInstance().copyFromVersion(versionedNode, node, new RuleBasedContentFilter(rule));
342                      node.save();
343                  }
344              }
345              catch (RepositoryException re) {
346                  log.error("failed to restore versioned node, reverting all changes make to this node");
347                  node.refresh(false);
348                  throw re;
349              }
350          }
351          catch (IOException e) {
352              throw new RepositoryException(e);
353          }
354          catch (ClassNotFoundException JavaDoc e) {
355              throw new RepositoryException(e);
356          }
357          catch (RepositoryException e) {
358              throw e;
359          }
360          finally {
361              this.revertAccessManager(permissions);
362          }
363      }
364
365      /**
366       * Removes all versions of the node associated with given UUID
367       * @param uuid
368       * @throws RepositoryException if fails to remove versioned node from the version store
369       */

370      public synchronized void removeVersionHistory(String JavaDoc uuid) throws RepositoryException {
371          List JavaDoc permissions = this.getAccessManagerPermissions();
372          this.impersonateAccessManager(null);
373          try {
374              Content node = this.getVersionedNode(uuid);
375              if (node != null) {
376                  if (node.getJCRNode().getReferences().getSize() < 1) {
377                      // remove node from the version store only if its not referenced
378
node.delete();
379                  } else { // remove all associated versions
380
VersionHistory history = node.getVersionHistory();
381                      VersionIterator versions = node.getAllVersions();
382                      if (versions != null) {
383                          // skip root version
384
versions.nextVersion();
385                          while (versions.hasNext()) {
386                              history.removeVersion(versions.nextVersion().getName());
387                          }
388                      }
389                  }
390              }
391          }
392          catch (RepositoryException re) {
393              throw re;
394          }
395          finally {
396              this.revertAccessManager(permissions);
397          }
398          getHierarchyManager().save();
399      }
400
401      /**
402       * get Rule used for this version
403       * @param versionedNode
404       * @throws IOException
405       * @throws ClassNotFoundException
406       * @throws RepositoryException
407       */

408      protected Rule getUsedFilter(Content versionedNode) throws IOException, ClassNotFoundException JavaDoc, RepositoryException {
409          // if restored, update original node with the restored node and its subtree
410
ByteArrayInputStream inStream = null;
411          try {
412              String JavaDoc ruleString = this.getSystemNode(versionedNode).getNodeData(PROPERTY_RULE).getString();
413              inStream = new ByteArrayInputStream(Base64.decodeBase64(ruleString.getBytes()));
414              ObjectInput objectInput = new ObjectInputStream(inStream);
415              return (Rule) objectInput.readObject();
416          }
417          catch (IOException e) {
418              throw e;
419          }
420          catch (ClassNotFoundException JavaDoc e) {
421              throw e;
422          }
423              finally {
424              IOUtils.closeQuietly(inStream);
425          }
426      }
427
428      /**
429       * get magnolia system node created under the given node
430       * @param node
431       * @throws RepositoryException if failed to create system node
432       */

433      protected synchronized Content getSystemNode(Content node) throws RepositoryException {
434          try {
435              return node.getContent(SYSTEM_NODE);
436          }
437          catch (PathNotFoundException e) {
438              return node.createContent(SYSTEM_NODE, ItemType.SYSTEM);
439          }
440      }
441
442      /**
443       * impersonate to be access manager with system rights
444       * @param permissions
445       */

446      protected void impersonateAccessManager(List JavaDoc permissions) {
447          this.getHierarchyManager().getAccessManager().setPermissionList(permissions);
448      }
449
450      /**
451       * revert access manager permissions
452       * @param permissions
453       */

454      protected void revertAccessManager(List JavaDoc permissions) {
455          this.getHierarchyManager().getAccessManager().setPermissionList(permissions);
456      }
457
458      /**
459       * get access manager permission list
460       */

461      protected List JavaDoc getAccessManagerPermissions() {
462          return this.getHierarchyManager().getAccessManager().getPermissionList();
463      }
464
465      /**
466       * get version store hierarchy manager
467       */

468      protected HierarchyManager getHierarchyManager() {
469          return MgnlContext.getHierarchyManager(VersionManager.VERSION_WORKSPACE);
470      }
471
472
473 }
474
Popular Tags