View Javadoc
1   /**
2    * This file Copyright (c) 2003-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.cms.core.version;
35  
36  import info.magnolia.cms.core.AbstractContent;
37  import info.magnolia.cms.core.Content;
38  import info.magnolia.cms.core.DefaultContent;
39  import info.magnolia.cms.core.HierarchyManager;
40  import info.magnolia.cms.core.ItemType;
41  import info.magnolia.cms.core.NodeData;
42  import info.magnolia.cms.security.AccessDeniedException;
43  import info.magnolia.cms.security.AccessManager;
44  import info.magnolia.cms.security.Permission;
45  import info.magnolia.cms.util.ContentWrapper;
46  import info.magnolia.cms.util.NodeDataWrapper;
47  import info.magnolia.cms.util.Rule;
48  import info.magnolia.objectfactory.Components;
49  import info.magnolia.repository.RepositoryManager;
50  
51  import java.util.ArrayList;
52  import java.util.Calendar;
53  import java.util.Collection;
54  import java.util.Comparator;
55  import java.util.List;
56  
57  import javax.jcr.Node;
58  import javax.jcr.PathNotFoundException;
59  import javax.jcr.PropertyType;
60  import javax.jcr.RepositoryException;
61  import javax.jcr.Value;
62  import javax.jcr.Workspace;
63  import javax.jcr.lock.Lock;
64  import javax.jcr.lock.LockException;
65  import javax.jcr.nodetype.NodeType;
66  import javax.jcr.nodetype.NodeTypeManager;
67  import javax.jcr.version.Version;
68  import javax.jcr.version.VersionHistory;
69  import javax.jcr.version.VersionIterator;
70  
71  import org.apache.commons.lang3.StringUtils;
72  import org.slf4j.Logger;
73  import org.slf4j.LoggerFactory;
74  
75  
76  /**
77   * Wraps a versioned node (frozen node) and allows traversing the hierarchy as if the node where in the original place.
78   *
79   * @deprecated since 4.5, declared deprecated in 5.6. Use {@link info.magnolia.cms.core.version.VersionedNode} instead.
80   */
81  @Deprecated
82  public class ContentVersion extends DefaultContent {
83  
84      /**
85       * Makes sure that the handle hides the fact that the nodes live in the version store.
86       */
87      private final class ContentVersionChildWrapper extends ContentWrapper {
88          private final Content parent;
89  
90          private ContentVersionChildWrapper(Content wrappedContent, Content parent) {
91              super(wrappedContent);
92              this.parent = parent;
93          }
94  
95          @Override
96          public Content getParent() throws RepositoryException {
97              return parent;
98          }
99  
100         /**
101          * Show the original path not the one from the version store.
102          */
103         @Override
104         public String getHandle() {
105             try {
106                 return getParent().getHandle() + "/" + getName();
107             } catch (RepositoryException e) {
108                 throw new RuntimeException("Can't create handle for versioned node.", e);
109             }
110         }
111 
112         /**
113          * We have to wrap the node data to make sure that the handle is correct.
114          */
115         @Override
116         public NodeData newNodeDataInstance(String name, int type, boolean createIfNotExisting) throws AccessDeniedException, RepositoryException {
117             return new NodeDataWrapper(super.newNodeDataInstance(name, type, createIfNotExisting)) {
118                 @Override
119                 public String getHandle() {
120                     return ContentVersionChildWrapper.this.getHandle() + "/" + getName();
121                 }
122             };
123         }
124 
125         @Override
126         protected Content wrap(Content node) {
127             return new ContentVersionChildWrapper(node, this);
128         }
129     }
130 
131     private static Logger log = LoggerFactory.getLogger(ContentVersion.class);
132 
133     /**
134      * User who created this version.
135      */
136     public static final String VERSION_USER = "versionUser";
137 
138     /**
139      * Name of the base node.
140      */
141     public static final String NAME = "name";
142 
143     /**
144      * Version node (nt:version).
145      */
146     private final Version state;
147 
148     /**
149      * The node as existing in the workspace. Not the version node.
150      */
151     private final Content base;
152 
153     /**
154      * Rule used to create this version.
155      */
156     private Rule rule;
157 
158     private final Version versionedNode;
159 
160     /**
161      * @deprecated since 5.4
162      */
163     @Deprecated
164     public ContentVersion(Version thisVersion, Content base) throws RepositoryException {
165         this(Components.getComponent(RepositoryManager.class), thisVersion, base);
166     }
167 
168     public ContentVersion(RepositoryManager repositoryManager, Version thisVersion, Content base) throws RepositoryException {
169         super(repositoryManager);
170 
171         if (thisVersion == null) {
172             throw new RepositoryException("Failed to get ContentVersion, version does not exist");
173         }
174         this.versionedNode = thisVersion;
175         if (thisVersion instanceof VersionedNode) {
176             this.state = ((VersionedNode) thisVersion).unwrap();
177         } else {
178             this.state = thisVersion;
179         }
180         this.base = base;
181 
182         this.init();
183     }
184 
185     /**
186      * Set frozen node of this version as working node.
187      */
188     private void init() throws RepositoryException {
189         this.setNode(this.state.getNode(ItemType.JCR_FROZENNODE));
190         try {
191             if (!StringUtils.equalsIgnoreCase(this.state.getName(), VersionManager.ROOT_VERSION)) {
192                 this.rule = VersionManager.getInstance().getUsedFilter(this.getJCRNode());
193             }
194         } catch (Exception e) {
195             log.error(e.getMessage(), e);
196         }
197         if (this.rule == null) {
198             log.info("failed to get filter used for creating this version, use open filter");
199             this.rule = new Rule();
200         }
201     }
202 
203     /**
204      * Get creation date of this version.
205      *
206      * @return creation date as calendar
207      */
208     public Calendar getCreated() throws RepositoryException {
209         return this.state.getCreated();
210     }
211 
212     /**
213      * Return the name of the version represented by this object.
214      *
215      * @return the versions name
216      */
217     public String getVersionLabel() throws RepositoryException {
218         return this.state.getName();
219     }
220 
221     /**
222      * Get containing version history.
223      *
224      * @return version history associated to this version
225      */
226     public VersionHistory getContainingHistory() throws RepositoryException {
227         return this.state.getContainingHistory();
228     }
229 
230     /**
231      * The original name of the node.
232      */
233     @Override
234     public String getName() {
235         try {
236             return VersionManager.getInstance().getSystemNode(this.getJCRNode()).getProperty(NAME).getString();
237         } catch (RepositoryException re) {
238             log.error("Failed to retrieve name from version system node", re);
239             return "";
240         }
241     }
242 
243     /**
244      * The name of the user who created this version.
245      */
246     public String getUserName() {
247         try {
248             return VersionManager.getInstance().getSystemNode(this.getJCRNode()).getProperty(VERSION_USER).getString();
249         } catch (RepositoryException re) {
250             log.error("Failed to retrieve user from version system node", re);
251             return "";
252         }
253     }
254 
255     /**
256      * Get original path of this versioned content.
257      */
258     @Override
259     public String getHandle() {
260         return this.base.getHandle();
261     }
262 
263     /**
264      * Returns a direct child if it was included in the version. Otherwise it tries to get the child from the original place.
265      * The versioning rule is respected.
266      */
267     @Override
268     public Content getContent(String name) throws PathNotFoundException, RepositoryException, AccessDeniedException {
269         //first we have to check if this is a direct child
270         if (super.hasContent(name)) {
271             return new ContentVersionChildWrapper(super.getContent(name), this);
272         }
273         Content content = base.getContent(name);
274         // only return the node if it was excluded from the versioning, otherwise the node is new
275         if (!rule.isAllowed(content.getJCRNode())) {
276             return content;
277         }
278         throw new PathNotFoundException(base.getHandle() + "/" + name);
279     }
280 
281     /**
282      * Uses the same approach as {@link #getContent(String)}.
283      */
284     @Override
285     public boolean hasContent(String name) throws RepositoryException {
286         if (super.hasContent(name)) {
287             return true;
288         } else if (base.hasContent(name)) {
289             Content content = base.getContent(name);
290             // only return the node if it was excluded from the versioning, otherwise the node is new
291             if (!rule.isAllowed(content.getJCRNode())) {
292                 return true;
293             }
294         }
295         return false;
296     }
297 
298     /**
299      * Throws an {@link AccessDeniedException} as versions are read only.
300      */
301     @Override
302     public Content createContent(String name) throws AccessDeniedException {
303         throw new AccessDeniedException("Not allowed to write on version preview");
304     }
305 
306     /**
307      * Throws an {@link AccessDeniedException} as versions are read only.
308      */
309     @Override
310     public Content createContent(String name, String contentType) throws AccessDeniedException {
311         throw new AccessDeniedException("Not allowed to write on version preview");
312     }
313 
314     /**
315      * Throws an {@link AccessDeniedException} as versions are read only.
316      */
317     @Override
318     public Content createContent(String name, ItemType contentType) throws AccessDeniedException {
319         throw new AccessDeniedException("Not allowed to write on version preview");
320     }
321 
322     /**
323      * Throws an {@link AccessDeniedException} as versions are read only.
324      */
325     @Override
326     public NodeData createNodeData(String name) throws AccessDeniedException {
327         throw new AccessDeniedException("Not allowed to write on version preview");
328     }
329 
330     /**
331      * Throws an {@link AccessDeniedException} as versions are read only.
332      */
333     public NodeData createNodeData(String name, Value value, int type) throws AccessDeniedException {
334         throw new AccessDeniedException("Not allowed to write on version preview");
335     }
336 
337     /**
338      * Throws an {@link AccessDeniedException} as versions are read only.
339      */
340     @Override
341     public NodeData createNodeData(String name, Value value) throws AccessDeniedException {
342         throw new AccessDeniedException("Not allowed to write on version preview");
343     }
344 
345     /**
346      * Throws an {@link AccessDeniedException} as versions are read only.
347      */
348     @Override
349     public NodeData createNodeData(String name, int type) throws AccessDeniedException {
350         throw new AccessDeniedException("Not allowed to write on version preview");
351     }
352 
353     /**
354      * Throws an {@link AccessDeniedException} as versions are read only.
355      */
356     @Override
357     public void deleteNodeData(String name) throws RepositoryException {
358         throw new AccessDeniedException("Not allowed to write on version preview");
359     }
360 
361     /**
362      * Throws an {@link AccessDeniedException} as versions are read only.
363      */
364     @Override
365     public void updateMetaData() throws AccessDeniedException {
366         throw new AccessDeniedException("Not allowed to write on version preview");
367     }
368 
369     /**
370      * All {@link #getChildren()} methods delegate to this method. We combine the direct children and children
371      * from the current node which were not included by the version rule.
372      */
373     @Override
374     public Collection<Content> getChildren(ContentFilter filter, String namePattern, Comparator<Content> orderCriteria) {
375         ArrayList<Content> result = new ArrayList<Content>();
376         result.addAll(wrap(super.getChildren(filter, namePattern, orderCriteria)));
377 
378         Collection<Content> transientChildren = ((AbstractContent) this.base).getChildren(filter, namePattern, orderCriteria);
379         for (Content transientChild : transientChildren) {
380             try {
381                 if (!rule.isAllowed(transientChild.getJCRNode())) {
382                     result.add(transientChild);
383                 }
384             } catch (RepositoryException e) {
385                 throw new RuntimeException("Can't determine node type of " + transientChild, e);
386             }
387         }
388 
389         return result;
390     }
391 
392     private Collection<Content> wrap(Collection<Content> children) {
393         List<Content> transformed = new ArrayList<Content>();
394         for (Content child : children) {
395             transformed.add(new ContentVersionChildWrapper(child, this));
396         }
397         return transformed;
398     }
399 
400     /**
401      * @return Boolean, if sub node(s) exists
402      */
403     @Override
404     public boolean hasChildren() {
405         return (this.getChildren().size() > 0);
406     }
407 
408     /**
409      * @param contentType JCR node type as configured
410      * @return Boolean, if sub <code>collectionType</code> exists
411      */
412     @Override
413     public boolean hasChildren(String contentType) {
414         return (this.getChildren(contentType).size() > 0);
415     }
416 
417     /**
418      * Returns the parent of the base node.
419      */
420     @Override
421     public Content getParent() throws PathNotFoundException, RepositoryException, AccessDeniedException {
422         return this.base.getParent();
423     }
424 
425     @Override
426     public Content getAncestor(int level) throws PathNotFoundException, RepositoryException, AccessDeniedException {
427         return this.base.getAncestor(level);
428     }
429 
430     /**
431      * Convenience method for taglib.
432      *
433      * @return Content representing node on level 0
434      * @throws javax.jcr.RepositoryException if an error occurs
435      */
436     @Override
437     public Collection<Content> getAncestors() throws PathNotFoundException, RepositoryException {
438         return this.base.getAncestors();
439     }
440 
441     /**
442      * Get node level from the ROOT node : FIXME implement getDepth in javax.jcr.
443      *
444      * @return level at which current node exist, relative to the ROOT node
445      * @throws javax.jcr.RepositoryException if an error occurs
446      */
447     @Override
448     public int getLevel() throws PathNotFoundException, RepositoryException {
449         return this.base.getLevel();
450     }
451 
452     /**
453      * Throws an {@link AccessDeniedException} as versions are read only.
454      */
455     @Override
456     public void orderBefore(String srcName, String beforeName) throws RepositoryException {
457         throw new AccessDeniedException("Not allowed to write on version preview");
458     }
459 
460     /**
461      * This method returns the index of this node within the ordered set of its same-name sibling nodes. This index is
462      * the one used to address same-name siblings using the square-bracket notation, e.g., /a[3]/b[4]. Note that the
463      * index always starts at 1 (not 0), for compatibility with XPath. As a result, for nodes that do not have
464      * same-name-siblings, this method will always return 1.
465      *
466      * @return The index of this node within the ordered set of its same-name sibling nodes.
467      * @throws javax.jcr.RepositoryException if an error occurs
468      */
469     @Override
470     public int getIndex() throws RepositoryException {
471         return this.base.getIndex();
472     }
473 
474     /**
475      * Returns primary node type definition of the associated Node of this object.
476      *
477      * @throws RepositoryException if an error occurs
478      */
479     @Override
480     public NodeType getNodeType() throws RepositoryException {
481         log.warn("This is a Version node, it will always return NT_FROZEN as node type.");
482         log.warn("Use getNodeTypeName to retrieve base node primary type");
483         return super.getNodeType();
484     }
485 
486     /**
487      * Throws an {@link AccessDeniedException} as versions are read only.
488      */
489     @Override
490     public void restore(String versionName, boolean removeExisting) throws RepositoryException {
491         throw new AccessDeniedException("Not allowed to write on version preview");
492     }
493 
494     /**
495      * Throws an {@link AccessDeniedException} as versions are read only.
496      */
497     @Override
498     public void restore(Version version, boolean removeExisting) throws RepositoryException {
499         throw new AccessDeniedException("Not allowed to write on version preview");
500     }
501 
502     /**
503      * Throws an {@link AccessDeniedException} as versions are read only.
504      */
505     @Override
506     public void restore(Version version, String relPath, boolean removeExisting) throws RepositoryException {
507         throw new AccessDeniedException("Not allowed to write on version preview");
508     }
509 
510     /**
511      * Throws an {@link AccessDeniedException} as versions are read only.
512      */
513     @Override
514     public void restoreByLabel(String versionLabel, boolean removeExisting) throws RepositoryException {
515         throw new AccessDeniedException("Not allowed to write on version preview");
516     }
517 
518     /**
519      * Throws an {@link AccessDeniedException} as versions are read only.
520      */
521     @Override
522     public Version addVersion() throws RepositoryException {
523         throw new AccessDeniedException("Not allowed to add version on version preview");
524     }
525 
526     /**
527      * Throws an {@link AccessDeniedException} as versions are read only.
528      */
529     @Override
530     public Version addVersion(Rule rule) throws RepositoryException {
531         throw new AccessDeniedException("Not allowed to add version on version preview");
532     }
533 
534     /**
535      * Returns always false as verions are read only.
536      */
537     @Override
538     public boolean isModified() {
539         log.error("Not valid for version");
540         return false;
541     }
542 
543     /**
544      * Throws an {@link AccessDeniedException} as versions are read only.
545      */
546     @Override
547     public VersionHistory getVersionHistory() throws RepositoryException {
548         throw new AccessDeniedException("Not allowed to read VersionHistory of Version");
549     }
550 
551     /**
552      * Throws an {@link AccessDeniedException} as versions are read only.
553      */
554     @Override
555     public VersionIterator getAllVersions() throws RepositoryException {
556         throw new AccessDeniedException("Not allowed to get VersionIterator of Version");
557     }
558 
559     /**
560      * Throws an {@link AccessDeniedException} as versions are read only.
561      */
562     @Override
563     public ContentVersion getBaseVersion() throws RepositoryException {
564         throw new AccessDeniedException("Not allowed to get base version of Version");
565     }
566 
567     /**
568      * Throws an {@link AccessDeniedException} as versions are read only.
569      */
570     @Override
571     public ContentVersion getVersionedContent(Version version) throws RepositoryException {
572         throw new AccessDeniedException("Not allowed to get preview of Version itself");
573     }
574 
575     /**
576      * Throws an {@link AccessDeniedException} as versions are read only.
577      */
578     @Override
579     public ContentVersion getVersionedContent(String versionName) throws RepositoryException {
580         throw new AccessDeniedException("Not allowed to get preview of Version itself");
581     }
582 
583     /**
584      * Throws an {@link AccessDeniedException} as versions are read only.
585      */
586     @Override
587     public void save() throws RepositoryException {
588         throw new AccessDeniedException("Not allowed to write on version preview");
589     }
590 
591     /**
592      * Checks for the allowed access rights.
593      *
594      * @param permissions as defined in javax.jcr.Permission
595      * @return true is the current user has specified access on this node.
596      */
597     @Override
598     public boolean isGranted(long permissions) {
599         return (permissions & Permission.READ) == permissions;
600     }
601 
602     /**
603      * Throws an {@link AccessDeniedException} as versions are read only.
604      */
605     @Override
606     public void delete() throws RepositoryException {
607         throw new AccessDeniedException("Not allowed to write on version preview");
608     }
609 
610     /**
611      * Throws an {@link AccessDeniedException} as versions are read only.
612      */
613     @Override
614     public void delete(String path) throws RepositoryException {
615         throw new AccessDeniedException("Not allowed to write on version preview");
616     }
617 
618     /**
619      * UUID of the node referenced by this object.
620      *
621      * @return the uuid
622      */
623     @Override
624     public String getUUID() {
625         return this.base.getUUID();
626     }
627 
628     /**
629      * Throws an {@link AccessDeniedException} as versions are read only.
630      */
631     @Override
632     public void addMixin(String type) throws RepositoryException {
633         throw new AccessDeniedException("Not allowed to write on version preview");
634     }
635 
636     /**
637      * Throws an {@link AccessDeniedException} as versions are read only.
638      */
639     @Override
640     public void removeMixin(String type) throws RepositoryException {
641         throw new AccessDeniedException("Not allowed to write on version preview");
642     }
643 
644     /**
645      * Throws an {@link AccessDeniedException} as versions are read only.
646      */
647     @Override
648     public Lock lock(boolean isDeep, boolean isSessionScoped) throws LockException, RepositoryException {
649         throw new AccessDeniedException("Lock not supported on version preview");
650     }
651 
652     /**
653      * Throws an {@link AccessDeniedException} as versions are read only.
654      */
655     @Override
656     public Lock lock(boolean isDeep, boolean isSessionScoped, long yieldFor) throws LockException, RepositoryException {
657         throw new AccessDeniedException("Lock not supported on version preview");
658     }
659 
660     /**
661      * Throws an {@link AccessDeniedException} as versions are read only.
662      */
663     @Override
664     public Lock getLock() throws LockException, RepositoryException {
665         throw new AccessDeniedException("Lock not supported on version preview");
666     }
667 
668     /**
669      * Throws an {@link AccessDeniedException} as versions are read only.
670      */
671     @Override
672     public void unlock() throws LockException, RepositoryException {
673         throw new AccessDeniedException("Lock not supported on version preview");
674     }
675 
676     /**
677      * Throws an {@link AccessDeniedException} as versions are read only.
678      */
679     @Override
680     public boolean holdsLock() throws RepositoryException {
681         throw new AccessDeniedException("Lock not supported on version preview");
682     }
683 
684     /**
685      * Throws an {@link AccessDeniedException} as versions are read only.
686      */
687     @Override
688     public boolean isLocked() throws RepositoryException {
689         throw new AccessDeniedException("Lock not supported on version preview");
690     }
691 
692     /**
693      * Get hierarchy manager if previously set for this object.
694      */
695     @Override
696     public HierarchyManager getHierarchyManager() {
697         return this.base.getHierarchyManager();
698     }
699 
700     /**
701      * Get access manager if previously set for this object.
702      *
703      * @deprecated use getHierarchyManager instead
704      */
705     @Deprecated
706     @Override
707     public AccessManager getAccessManager() {
708         return this.base.getAccessManager();
709     }
710 
711     @Override
712     public Workspace getWorkspace() throws RepositoryException {
713         return this.base.getWorkspace();
714     }
715 
716     @Override
717     public boolean hasNodeData(String name) throws RepositoryException {
718         if (this.node.hasProperty(name)) {
719             return true;
720         }
721         if (this.node.hasNode(name) && this.node.getNode(name).getProperty("jcr:frozenPrimaryType").getValue().getString().equals(ItemType.NT_RESOURCE)) {
722             return true;
723         }
724         return false;
725     }
726 
727     @Override
728     protected int determineNodeDataType(String name) {
729         // FIXME: maybe delegate to NodeDataImplementations?
730         try {
731             if (this.node.hasProperty(name)) {
732                 return this.node.getProperty(name).getType();
733             }
734             if (this.node.hasNode(name) && this.node.getNode(name).getProperty("jcr:frozenPrimaryType").getValue().getString().equals(ItemType.NT_RESOURCE)) {
735                 return PropertyType.BINARY;
736             }
737         } catch (RepositoryException e) {
738             throw new IllegalStateException("Can't determine property type of [" + getHandle() + "/" + name + "]", e);
739         }
740         return PropertyType.UNDEFINED;
741     }
742 
743     @Override
744     public NodeType[] getMixinNodeTypes() throws RepositoryException {
745         Value[] vals = this.node.getProperty("jcr:frozenMixinTypes").getValues();
746         NodeTypeManager typeMan = getJCRNode().getSession().getWorkspace().getNodeTypeManager();
747         NodeType[] types = new NodeType[vals.length];
748         int i = 0;
749         for (Value val : vals) {
750             types[i++] = typeMan.getNodeType(val.getString());
751         }
752         return types;
753     }
754 
755     //    public List<ContentVersion> getPredecessors() throws RepositoryException {
756     //        List<ContentVersion> list = new ArrayList<ContentVersion>();
757     //        for (Version v : this.state.getPredecessors()) {
758     //            list.add(new ContentVersion(v, this.base));
759     //        }
760     //        return list;
761     //    }
762     public Version[] getPredecessors() throws RepositoryException {
763         // would prefer to return the above version (List of ContentVersions), but since our other APIs return jcr Version as well ...
764         return this.state.getPredecessors();
765     }
766 
767     @Override
768     public Node getJCRNode() {
769         // we seem to build content version from 2 different types of nodes - from Version and from jcr:frozenNode
770         try {
771             if (versionedNode.hasNode("jcr:frozenNode")) {
772                 return versionedNode.getFrozenNode();
773             }
774         } catch (RepositoryException e) {
775             log.error("Failed to retrieve frozen node from version {}", versionedNode, e);
776         }
777 
778         return versionedNode;
779     }
780 }