com.thinkbiganalytics.metadata.modeshape.support.JcrVersionUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.thinkbiganalytics.metadata.modeshape.support.JcrVersionUtil.java

Source

package com.thinkbiganalytics.metadata.modeshape.support;

/*-
 * #%L
 * thinkbig-metadata-modeshape
 * %%
 * Copyright (C) 2017 ThinkBig Analytics
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.thinkbiganalytics.metadata.modeshape.JcrMetadataAccess;
import com.thinkbiganalytics.metadata.modeshape.MetadataRepositoryException;
import com.thinkbiganalytics.metadata.modeshape.common.JcrObject;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import javax.jcr.AccessDeniedException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.LockManager;
import javax.jcr.nodetype.NodeType;
import javax.jcr.version.Version;
import javax.jcr.version.VersionHistory;
import javax.jcr.version.VersionIterator;
import javax.jcr.version.VersionManager;

import org.modeshape.jcr.JcrLexicon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 */
public class JcrVersionUtil {

    private static final Logger log = LoggerFactory.getLogger(JcrVersionUtil.class);

    public static Node createAutoCheckoutProxy(Node node) {
        InvocationHandler handler = new VersionableNodeInvocationHandler(node);
        return (Node) Proxy.newProxyInstance(Node.class.getClassLoader(), new Class<?>[] { Node.class }, handler);
    }

    /**
     * Returns the LockManager object from the given session.
     *
     * @param session {@link Session}
     * @return a {@link LockManager} object
     */
    public static LockManager getLockManager(Session session) throws RepositoryException {
        LockManager lockMgr = session.getWorkspace().getLockManager();
        return lockMgr;
    }

    /**
     * Returns the VersionManager object from the given session.
     *
     * @param session {@link Session}
     * @return a {@link VersionManager} object
     */
    public static VersionManager getVersionManager(Session session) throws RepositoryException {
        VersionManager versionMgr = session.getWorkspace().getVersionManager();
        return versionMgr;
    }

    /**
     * Ensures that the given node, or a versionable ancestor, is checked out.
     * Passing a node that is not in a versionable hierarch has no effect.
     * @param n the node to whose hierarchy is to be checked out
     */
    public static void ensureCheckoutNode(Node node) {
        getVersionableAncestor(node).ifPresent(a -> JcrMetadataAccess.ensureCheckoutNode(a));
    }

    /**
     * Finds the first node in the argument node's parentage that is versionable, if any.  If the argument 
     * node itself is versionable then an Optional containing it is returned. If there is no versionable
     * node in this node's hierarchy then an empty Optional is returned.
     * @param node the node who's parentage is to be searched
     * @return An Optional containing the closest versionable parent of the node if it exists, otherwise an empty Optional
     */
    public static Optional<Node> getVersionableAncestor(Node node) {
        try {
            if (node.getSession().getRootNode().getIdentifier().equals(node.getParent().getIdentifier())) {
                return Optional.empty();
            } else if (isVersionable(node)) {
                return Optional.of(node);
            } else {
                return getVersionableAncestor(node.getParent());
            }
        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Unable to check if node is in versionable hierarchy", e);
        }
    }

    /**
     * Checks if the given node is within a versionable hierarchy.
     * @param node he node who's parentage is to be searched
     * @return true if this node or one of its parents is versionable, otherwise false
     */
    public static boolean inVersionableHierarchy(Node node) {
        return getVersionableAncestor(node).map(n -> true).orElse(false);
    }

    /**
     * @param node the node to test
     * @return true if this node is versionable
     */
    public static boolean isVersionable(Node node) {
        String name = "";
        boolean versionable = false;
        try {
            name = node.getName();
            versionable = JcrUtil.hasMixinType(node, "mix:versionable");
            return versionable;
        } catch (AccessDeniedException e) {
            log.debug("Access denied", e);
            throw new AccessControlException(e.getMessage());
        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Unable to check if versionable for Node " + name, e);
        }
    }

    /**
     * Sets the given node to checked-out status.
     *
     * @param node node to check-out
     */
    public static void checkout(Node node) throws RepositoryException {
        getVersionManager(node.getSession()).checkout(node.getPath());
    }

    /**
     * Creates for the given node a new version and returns that version. Put the node into the checked-in state.
     *
     * @param node node to checkin
     * @return the created version
     */
    public static Version checkin(Node node) throws RepositoryException {
        try {
            // Checking if the checked-out property exists fixes a ModeShape bug where this property is not checked
            // if it is null on check-in as the node.isCheckedOut() method does.  This can be null
            // when the node being checked-in has not been saved after being just made versionable; which 
            // can occur during upgrade to 0.8.4.
            if (node.isCheckedOut() && JcrPropertyUtil.hasProperty(node, JcrLexicon.IS_CHECKED_OUT.getString())) {
                return getVersionManager(node.getSession()).checkin(node.getPath());
            } else {
                return null;
            }
        } catch (ItemNotFoundException e) {
            // Ignore if the node being checked in represents a deleted node.
            return null;
        }
    }

    public static void checkinRecursively(Node node) {
        try {
            NodeIterator it = node.getNodes();
            while (it.hasNext()) {
                checkinRecursively(it.nextNode());
            }
            if (node.isCheckedOut() && node.isNodeType(NodeType.MIX_VERSIONABLE)) {
                checkin(node);
            }

        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Could not perform check-in", e);
        }
    }

    public static void checkoutRecursively(Node node) {
        try {
            NodeIterator it = node.getNodes();
            while (it.hasNext()) {
                checkoutRecursively(it.nextNode());
            }
            if (!node.isCheckedOut() && node.isNodeType(NodeType.MIX_VERSIONABLE)) {
                checkout(node);
            }

        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Could not perform check-out", e);
        }
    }

    /**
     * Places a lock on the given node.
     *
     * @param node            the node to be locked
     * @param isDeep          if <code>true</code> this lock will apply to this node and all its descendants; if <code>false</code>, it applies only to this node.
     * @param isSessionScoped if <code>true</code>, this lock expires with the current session; if <code>false</code> it expires when explicitly or automatically unlocked for some other reason.
     * @param timeoutHint     desired lock timeout in seconds (servers are free to ignore this value); specify Long.MAX_VALUE for no timeout.
     * @param ownerInfo       a string containing owner information supplied by the client; servers are free to ignore this value.
     * @see javax.jcr.lock.LockManager#lock(String, boolean, boolean, long, String)
     */
    public static void lock(Node node, boolean isDeep, boolean isSessionScoped, long timeoutHint,
            String ownerInfo) {
        try {
            getLockManager(node.getSession()).lock(node.getPath(), isDeep, isSessionScoped, timeoutHint, ownerInfo);
        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Could not perform lock", e);
        }
    }

    /**
     * Removes the lock on the given node.
     *
     * @param node the node unlock
     * @see javax.jcr.lock.LockManager#unlock(String)
     */
    public static void unlock(Node node) {
        try {
            getLockManager(node.getSession()).unlock(node.getPath());
        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Could not perform unlock", e);
        }
    }

    public static Version getBaseVersion(Node node) {
        String nodeName = null;
        if (!isVersionable(node)) {
            return null;
        }
        try {
            nodeName = node.getName();
            return JcrVersionUtil.getVersionManager(node.getSession()).getBaseVersion(node.getPath());
        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Unable to find Base Version for " + nodeName, e);
        }
    }

    public static List<Version> getVersions(Node node) {
        String nodeName = null;
        if (!isVersionable(node)) {
            return null;
        }
        try {
            nodeName = node.getName();
            List<Version> versions = new ArrayList<>();
            VersionHistory history = JcrVersionUtil.getVersionManager(node.getSession())
                    .getVersionHistory(node.getPath());
            VersionIterator itr = history.getAllVersions();
            String id = history.getVersionableIdentifier();

            while (itr.hasNext()) {
                Version version = itr.nextVersion();
                versions.add(version);
            }
            return versions;
        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Unable to find Version History for " + nodeName, e);
        }

    }

    public static <T extends JcrObject> T getVersionedNode(Version version, Class<T> type,
            Object[] constructorArgs) {
        String nodeName = null;
        String versionName = null;

        try {
            versionName = version.getName();
            Node node = version.getFrozenNode();
            nodeName = node.getName();
            String entityId = version.getContainingHistory().getVersionableIdentifier();
            T obj = JcrUtil.constructNodeObject(node, type, constructorArgs);
            obj.setVersionName(versionName);
            obj.setVersionableIdentifier(entityId);
            return obj;
        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Unable to find Version " + versionName + " for Node " + nodeName,
                    e);
        }
    }

    public static Version findVersion(Node node, final String versionName) {
        if (!isVersionable(node)) {
            return null;
        }
        Version version = Iterables.tryFind(getVersions(node), new Predicate<Version>() {
            @Override
            public boolean apply(Version version) {
                try {
                    String identifier = node.getIdentifier();
                    String frozenIdentifer = JcrPropertyUtil.getString(version.getFrozenNode(), "jcr:frozenUuid");
                    return version.getName().equalsIgnoreCase(versionName)
                            && frozenIdentifer.equalsIgnoreCase(identifier);
                } catch (RepositoryException e) {

                }
                return false;
            }
        }).orNull();
        return version;
    }

    public static <T extends JcrObject> T getVersionedNode(Version version, Class<T> type) {
        return getVersionedNode(version, type, null);
    }

    public static <T extends JcrObject> T getVersionedNode(JcrObject node, String versionNumber, Class<T> type) {
        Version version = findVersion(node.getNode(), versionNumber);
        if (version != null) {
            return getVersionedNode(version, type);
        }
        throw new MetadataRepositoryException(
                "Unable to find Version " + versionNumber + " for Node " + node.getNodeName());
    }

    public static Node getFrozenNode(Version version) {
        try {
            return version.getFrozenNode();
        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Unable to get frozen node of Version: " + version, e);
        }
    }

}