Package org.jahia.services.content

Source Code of org.jahia.services.content.JCRVersionService

/**
* This file is part of Jahia, next-generation open source CMS:
* Jahia's next-generation, open source CMS stems from a widely acknowledged vision
* of enterprise application convergence - web, search, document, social and portal -
* unified by the simplicity of web content management.
*
* For more information, please visit http://www.jahia.com.
*
* Copyright (C) 2002-2011 Jahia Solutions Group SA. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* As a special exception to the terms and conditions of version 2.0 of
* the GPL (or any later version), you may redistribute this Program in connection
* with Free/Libre and Open Source Software ("FLOSS") applications as described
* in Jahia's FLOSS exception. You should have received a copy of the text
* describing the FLOSS exception, and it is also available here:
* http://www.jahia.com/license
*
* Commercial and Supported Versions of the program (dual licensing):
* alternatively, commercial and supported versions of the program may be used
* in accordance with the terms and conditions contained in a separate
* written agreement between you and Jahia Solutions Group SA.
*
* If you are unsure which license is appropriate for your use,
* please contact the sales department at sales@jahia.com.
*/

package org.jahia.services.content;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.jahia.api.Constants;
import org.jahia.exceptions.JahiaException;
import org.jahia.exceptions.JahiaInitializationException;
import org.jahia.services.JahiaService;
import org.jahia.services.content.decorator.JCRFrozenNodeAsRegular;

import javax.jcr.*;
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 java.util.*;

/**
* Version listing and retrieval service. This service offers tools to list the versions in a user-friendly way,
* collapsing internal versions into date-related changes, retrieving comments, etc...
*
* @author loom
*         Date: Mar 10, 2010
*         Time: 10:06:02 AM
*/
public class JCRVersionService extends JahiaService {

    private static JCRVersionService instance;

    private static transient Logger logger = org.slf4j.LoggerFactory.getLogger(JCRVersionService.class);

    /**
     * Get the singleton instance of the JCRPublicationService
     *
     * @return the singleton instance of the JCRPublicationService
     */
    public synchronized static JCRVersionService getInstance() {
        if (instance == null) {
            instance = new JCRVersionService();
        }
        return instance;
    }


    @Override
    public void start() throws JahiaInitializationException {
    }

    @Override
    public void stop() throws JahiaException {
    }

    /**
     * Retrieves the list of versions, ignoring internal version created in the publication process.
     *
     * @param session the session to use to retrieve the versions
     * @param node    the node for which to retrieve the versions
     * @return a List of VersionInfo objects containing the resolved versions, as well as extra information such as the
     *         checkinDate if available.
     * @throws RepositoryException happens if there was a problem retrieving the list of versions.
     */
    public List<VersionInfo> getVersionInfos(Session session, JCRNodeWrapper node) throws RepositoryException {
        VersionHistory versionHistory = session.getWorkspace().getVersionManager().getVersionHistory(node.getPath());

        VersionIterator versions = versionHistory.getAllVersions();
        if (versions.hasNext()) {
            versions.nextVersion();
            // the first is the root version, which has no properties, so we will ignore it.
        }
        Set<VersionInfo> versionList = new TreeSet<VersionInfo>();
        while (versions.hasNext()) {
            Version v = versions.nextVersion();
            String[] versionLabels = versionHistory.getVersionLabels(v);
            if (versionLabels != null && versionLabels.length > 0) {
                for (String string : versionLabels) {
                    VersionInfo versionInfo = new VersionInfo(v, string, 0);
                    versionList.add(versionInfo);
                }
            }
        }
        return new ArrayList<VersionInfo>(versionList);
    }


    /**
     * Finds the closest version in a version history to a specific date.
     *
     * @param vh          the version history in which to lookup versions
     * @param versionDate the date to compare with. Note that it will find the closest version at OR BEFORE the date
     * @return the closest version at or before the date specified.
     * @throws RepositoryException
     */
    public static Version findClosestVersion(VersionHistory vh, Date versionDate) throws RepositoryException {
        VersionIterator vi = null;
        try {
            vi = vh.getAllLinearVersions();
        } catch (ItemNotFoundException e) {
            String[] labels = vh.getVersionLabels();
            for (String label : labels) {
                if (label.startsWith(vh.getSession().getWorkspace().getName()+"_removed")) {
                    Version base = vh.getVersionByLabel(label);
                    LinkedList<Version> versions = new LinkedList<Version>();
                    while (base != null) {
                        versions.addFirst(base);
                        Version[] preds = base.getPredecessors();
                        if (preds.length == 0) {
                            base = null;
                        } else {
                            base = preds[0];
                        }
                    }
                    vi = new VersionIteratorImpl(versions.iterator(), versions.size());
                    break;
                }
            }
            if (vi == null) {
                return null;
            }
        }


        Version lastVersion = null;
        Version closestVersion = null;
        if (vi.hasNext()) {
            vi.nextVersion();
            // the first is the root version, which has no properties, so we will ignore it.
        }
        String nodeTitle = null;
        StringBuffer propertyString = null;
        while (vi.hasNext()) {
            Version v = vi.nextVersion();
            if (logger.isDebugEnabled()) {
                try {
                    Node frozenNode = v.getFrozenNode();
                    propertyString = new StringBuffer();
                    PropertyIterator propertyIterator = frozenNode.getProperties();
                    while (propertyIterator.hasNext()) {
                        Property property = propertyIterator.nextProperty();
                        propertyString.append("  ");
                        propertyString.append(property.getName());
                        propertyString.append("=");
                        if (property.isMultiple()) {
                            for (Value value : property.getValues()) {
                                propertyString.append(value.getString());
                                propertyString.append(",");
                            }
                        } else {
                            propertyString.append(property.getValue().getString());
                        }
                        propertyString.append("\n");
                    }
                } catch (IllegalStateException e) {
                    propertyString.append(e.getMessage()).append("\n");
                }
            }
            Date checkinDate = null;
            boolean checkinDateAvailable = false;
            if (v.getCreated().getTime().compareTo(versionDate) > 0) {
                // this can happen if we have a checkinDate, but try to resolve using the creation date.
                closestVersion = lastVersion;
                break;
            }
            if (logger.isDebugEnabled()) {
                logger.debug(
                        "Version " + v.getName() + " checkinDateAvailable=" + checkinDateAvailable + " checkinDate=" +
                                checkinDate + " created=" + v.getCreated().getTime() + " properties:" +
                                propertyString.toString());
            }
            lastVersion = v;
        }
        if (closestVersion == null && lastVersion != null) {
            // if we haven't found anything, maybe it's the last version that we should be using ?
            if (lastVersion.getCreated().getTime().compareTo(versionDate) <= 0) {
                closestVersion = lastVersion;
            }
        }
        if (closestVersion!=null && logger.isDebugEnabled()) {
            logger.debug("Resolved date " + versionDate + " for node title " + nodeTitle + " to closest version " +
                    closestVersion.getName() + " createdTime=" + closestVersion.getCreated().getTime());
        }
        return closestVersion;
    }

    public void restoreVersionLabel(final JCRNodeWrapper node, final Date versionDate, final String label, final boolean allSubTree) throws RepositoryException {
        JCRTemplate.getInstance().doExecuteWithSystemSession(null, node.getSession().getWorkspace().getName(), null,
                new JCRCallback<Object>() {
                    public Object doInJCR(final JCRSessionWrapper session) throws RepositoryException {
                        String workspace = label!=null?StringUtils.substringBefore(label, "_"):"live";
                        JCRTemplate.getInstance().doExecuteWithSystemSession(null, workspace, null,
                                new JCRCallback<Object>() {
                                    public Object doInJCR(final JCRSessionWrapper frozensession) throws RepositoryException {
                                        JCRNodeWrapper destinationNode = session.getNodeByUUID(node.getIdentifier());
                                        VersionManager versionManager = session.getWorkspace().getVersionManager();
                                        String path = destinationNode.getPath();
                                        if (!versionManager.isCheckedOut(path)) {
                                            versionManager.checkout(path);
                                        }

                                        // Todo: first get frozen node for this label
                                        frozensession.setVersionLabel(label);
                                        frozensession.setVersionDate(versionDate);
                                        JCRNodeWrapper frozenVersionAsRegular = frozensession.getNodeByUUID(destinationNode.getIdentifier());

                                        if(frozenVersionAsRegular==null) {
                                            throw new RepositoryException("label version " + label + " could not be found on node "+destinationNode.getPath());
                                        }
                                        synchronizeNode(frozenVersionAsRegular, destinationNode, session, allSubTree);
                                        session.save();
                                        return null;
                                    }
                                });
                        return null;
                    }
                });
    }

    private void synchronizeNode(final JCRNodeWrapper frozenNode, final JCRNodeWrapper destinationNode,
                                 JCRSessionWrapper session, boolean allSubTree)
            throws RepositoryException {
        session.checkout(destinationNode);

        NodeType[] mixin = frozenNode.getMixinNodeTypes();
        for (NodeType aMixin : mixin) {
            if(!Constants.forbiddenMixinToCopy.contains(aMixin.getName())) {
                logger.info("Adding mixin "+aMixin.getName()+" on node "+destinationNode.getPath());
                destinationNode.addMixin(aMixin.getName());
            }
        }

        if (frozenNode.hasProperty("jcr:language")) {
            destinationNode.setProperty("jcr:language", frozenNode.getProperty("jcr:language").getString());
        }

        PropertyIterator props = frozenNode.getProperties();

        List<String> names = new ArrayList<String>();
        while (props.hasNext()) {
            Property property = props.nextProperty();
            String propertyName = property.getName();
            names.add(propertyName);
            logger.info("Checking property for updating "+propertyName+" from source node "+destinationNode.getPath());
            try {
                if (!property.getDefinition().isProtected() &&
                        !Constants.forbiddenPropertiesToCopy.contains(propertyName)) {
                    logger.info("Setting property "+propertyName+" on node "+destinationNode.getPath());
                    if (property.getDefinition().isMultiple() && property.isMultiple()) {
                        destinationNode.setProperty(propertyName, property.getValues());
                    } else {
                        destinationNode.setProperty(propertyName, property.getValue());
                    }
                }
            } catch (Exception e) {
                logger.warn("Unable to copy property '" + propertyName + "'. Skipping.", e);
            }
        }

        PropertyIterator pi = destinationNode.getProperties();
        while (pi.hasNext()) {
            JCRPropertyWrapper oldChild = (JCRPropertyWrapper) pi.next();
            logger.info("Checking property for removal "+oldChild.getName()+" from destination node "+destinationNode.getPath());
            if (!oldChild.getDefinition().isProtected()) {
                if (!names.contains(oldChild.getName())) {
                    logger.info("Removing property "+oldChild.getName()+" on node "+destinationNode.getPath());
                    oldChild.remove();
                }
            }
        }

        mixin = destinationNode.getMixinNodeTypes();
        for (NodeType aMixin : mixin) {
            if (!frozenNode.isNodeType(aMixin.getName()) &&
                    !Constants.forbiddenMixinToCopy.contains(aMixin.getName())) {
                logger.info("Removing mixin "+aMixin.getName()+" on node "+destinationNode.getPath());
                destinationNode.removeMixin(aMixin.getName());
            }
        }

        Map<String, JCRNodeWrapper> destinationNodes = new HashMap<String, JCRNodeWrapper>();
        NodeIterator ni = destinationNode.getNodes();
        while (ni.hasNext()) {
            JCRNodeWrapper n = (JCRNodeWrapper) ni.nextNode();
            destinationNodes.put(n.getIdentifier(), n);
        }

        names.clear();
        ni = frozenNode.getNodes();
        while (ni.hasNext()) {
            JCRNodeWrapper child = (JCRNodeWrapper) ni.next();
            // do not handle rights on the node when restore a node
            if (!child.getName().equals("j:acl")) {
                names.add(child.getName());
                if (destinationNodes.containsKey(child.getIdentifier())) {
                    JCRNodeWrapper node = destinationNodes.remove(child.getIdentifier());
                    synchronizeNode(child, node, session, allSubTree);
                } else if (child.getRealNode().getParent().isNodeType(Constants.NT_FROZENNODE)) {
                    JCRNodeWrapper node = destinationNode.addNode(child.getName(), child.getPrimaryNodeType().getName());
                    synchronizeNode(child, node, session, allSubTree);
                } else {
                    VersionHistory history;
                    try {
                        history = (VersionHistory) child.getRealNode().getProperty(
                                Constants.JCR_VERSIONHISTORY).getNode();
                    } catch (RepositoryException e){
                        history = (VersionHistory) child.getRealNode().getParent().getParent();
                    }
                    Version version = findVersionByLabel(history, ((JCRFrozenNodeAsRegular) child).getVersionLabel());
                    if (version == null) {
                        version = findClosestVersion(history, ((JCRFrozenNodeAsRegular) child).getVersionDate());
                    }
                    if (version != null) {
                        session.save();
                        logger.info("Restoring node "+child.getPath()+" on parent "+destinationNode.getPath());
                        session.getWorkspace().getVersionManager().restore(child.getPath(),version, false);
                        JCRNodeWrapper node = session.getNode(child.getPath(), false);
                        synchronizeNode(child, node, session, allSubTree);
                    }
                    //child.copy(destinationNode, child.getName(), false);
                }
            }
        }

        for (JCRNodeWrapper oldChild : destinationNodes.values()) {
            if (!names.contains(oldChild.getName())) {
                if ((!oldChild.isNodeType("jmix:publication") || allSubTree) && !oldChild.isNodeType("jnt:translation")) {
                    logger.info("Removing node "+oldChild.getName()+" on node "+destinationNode.getPath());
                    oldChild.remove();
                }
            }
        }

        if (destinationNode.getPrimaryNodeType().hasOrderableChildNodes()) {
            Collections.reverse(names);
            String previous = null;
            for (String name : names) {
                destinationNode.orderBefore(name, previous);
                previous = name;
            }
        }
        if (destinationNode.isNodeType(Constants.MIX_LAST_MODIFIED)) {
            destinationNode.setProperty(Constants.JCR_LASTMODIFIED,GregorianCalendar.getInstance());
        }
    }

    public static Version findVersionByLabel(VersionHistory vh, String label) throws RepositoryException {
        if (label != null && !"".equals(label.trim())) {
            if (vh.hasVersionLabel(label.trim())) {
                VersionIterator allVersions = vh.getAllVersions();
                while (allVersions.hasNext()) {
                    Version version = allVersions.nextVersion();
                    if (Arrays.asList(vh.getVersionLabels(version)).contains(label.trim())) {
                        return version;
                    }
                }
            }
        }
        return null;
    }

    public void addVersionLabel(final JCRNodeWrapper node, final String label) throws RepositoryException {
        JCRTemplate.getInstance().doExecuteWithSystemSession(null, node.getSession().getWorkspace().getName(), null,
                new JCRCallback<Object>() {
                    public Object doInJCR(JCRSessionWrapper session) throws RepositoryException {
                        JCRNodeWrapper nodeWrapper = session.getNodeByUUID(node.getIdentifier());
                        VersionManager versionManager = session.getWorkspace().getVersionManager();
                        VersionHistory versionHistory = versionManager.getVersionHistory(node.getPath());
                        String labelWithWs = node.getSession().getWorkspace().getName() + "_" + label;
                        if (!versionHistory.hasVersionLabel(labelWithWs)) {
                            Version version = versionManager.getBaseVersion(node.getPath());
                            logger.debug("Add version label " + labelWithWs + " on " + node.getPath() + " for version " +
                                    version.getName());
                            if (nodeWrapper.isVersioned()) {
                                versionHistory.addVersionLabel(version.getName(), labelWithWs, true);
                            }
                        }
                        return null;
                    }
                });

    }

    public void addVersionLabel(final List<String> allUuids, final String label, final String workspace)
            throws RepositoryException {
        JCRTemplate.getInstance().doExecuteWithSystemSession(null, workspace, null, new JCRCallback<Object>() {
            public Object doInJCR(JCRSessionWrapper session) throws RepositoryException {
                VersionManager versionManager = session.getWorkspace().getVersionManager();
                for (String allUuid : allUuids) {
                    try {
                        JCRNodeWrapper nodeWrapper = session.getNodeByUUID(allUuid);
                        VersionHistory versionHistory = versionManager.getVersionHistory(nodeWrapper.getPath());
                        String labelWithWs = workspace + "_" + label;
                        if (!versionHistory.hasVersionLabel(labelWithWs)) {
                            Version version = versionManager.getBaseVersion(nodeWrapper.getPath());
                            logger.debug("Add version label " + labelWithWs + " on " + nodeWrapper.getPath() + " for version " +
                                    version.getName());
                            if (nodeWrapper.isVersioned()) {
                                versionHistory.addVersionLabel(version.getName(), labelWithWs, true);
                            }
                        }
                    } catch (RepositoryException e) {
                        logger.debug(e.getMessage(), e);
                    }
                }
                return null;
            }
        });
    }
}
TOP

Related Classes of org.jahia.services.content.JCRVersionService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.