org.jahia.services.importexport.ReferencesHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.jahia.services.importexport.ReferencesHelper.java

Source

/**
 * ==========================================================================================
 * =                   JAHIA'S DUAL LICENSING - IMPORTANT INFORMATION                       =
 * ==========================================================================================
 *
 *                                 http://www.jahia.com
 *
 *     Copyright (C) 2002-2017 Jahia Solutions Group SA. All rights reserved.
 *
 *     THIS FILE IS AVAILABLE UNDER TWO DIFFERENT LICENSES:
 *     1/GPL OR 2/JSEL
 *
 *     1/ GPL
 *     ==================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE GPL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     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 3 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, see <http://www.gnu.org/licenses/>.
 *
 *
 *     2/ JSEL - Commercial and Supported Versions of the program
 *     ===================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE JSEL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     Alternatively, commercial and supported versions of the program - also known as
 *     Enterprise Distributions - must 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.importexport;

import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.util.ISO9075;
import org.apache.jackrabbit.util.Text;
import org.jahia.api.Constants;
import org.jahia.services.content.*;
import org.jahia.services.content.nodetypes.ExtendedPropertyDefinition;
import org.slf4j.Logger;

import javax.jcr.*;
import javax.jcr.nodetype.ConstraintViolationException;
import java.util.*;

/**
 * User: toto
 * Date: Dec 18, 2009
 * Time: 11:58:07 AM
 */
public class ReferencesHelper {
    private static Logger logger = org.slf4j.LoggerFactory.getLogger(ReferencesHelper.class);

    public static final int MAX_BATCH = 1000;

    public static void resolveCrossReferences(JCRSessionWrapper session, Map<String, List<String>> references)
            throws RepositoryException {
        resolveCrossReferences(session, references, true);
    }

    public static void resolveCrossReferences(JCRSessionWrapper session, Map<String, List<String>> references,
            boolean useReferencesKeeper) throws RepositoryException {
        resolveCrossReferences(session, references, useReferencesKeeper, false);
    }

    public static void resolveCrossReferences(JCRSessionWrapper session, Map<String, List<String>> references,
            boolean useReferencesKeeper, boolean keepReferencesForLive) throws RepositoryException {
        JCRSessionWrapper referencesKeeperSession = session;
        if (useReferencesKeeper) {
            referencesKeeperSession = resolveReferencesKeeper(session);
        }
        Map<String, String> uuidMapping = session.getUuidMapping();
        JCRNodeWrapper refRoot = referencesKeeperSession.getNode("/referencesKeeper");
        boolean resolved;
        List<String> resolvedUUIDStringList = new LinkedList<String>();
        for (String uuid : references.keySet()) {
            if (StringUtils.isBlank(uuid)) {
                continue;
            }
            final List<String> paths = references.get(uuid);
            resolved = true;
            if (uuidMapping.containsKey(uuid)) {
                update(paths, session, uuidMapping.get(uuid));
            } else {
                try {
                    if (uuid.startsWith("/")) {
                        uuid = ISO9075.decode(uuid);
                        for (Map.Entry<String, String> mapping : session.getPathMapping().entrySet()) {
                            if (uuid.startsWith(mapping.getKey())) {
                                uuid = mapping.getValue() + StringUtils.substringAfter(uuid, mapping.getKey());
                            }
                        }
                        uuid = JCRContentUtils.escapeNodePath(uuid);
                        JCRNodeWrapper node = session.getNode(uuid);
                        update(paths, session, node.getIdentifier());
                    } else {
                        session.getNodeByUUID(uuid);
                        // node was existing and is not in import, use old uuid
                        update(paths, session, uuid);
                    }
                } catch (PathNotFoundException e) {
                    if (useReferencesKeeper) {
                        // store reference for later
                        for (String path : paths) {
                            JCRNodeWrapper r = refRoot.addNode("j:reference" + UUID.randomUUID().toString(),
                                    "jnt:reference");
                            String refuuid = path.substring(0, path.lastIndexOf("/"));
                            String pName = path.substring(path.lastIndexOf("/") + 1);
                            r.setProperty(Constants.NODE, refuuid);
                            r.setProperty("j:propertyName", pName);
                            r.setProperty("j:originalUuid", uuid);
                            r.setProperty("j:live", keepReferencesForLive);
                        }
                        logger.warn(
                                "Reference to " + uuid + " cannot be resolved, store it in the reference keeper");
                    } else {
                        resolved = false;
                    }
                } catch (ItemNotFoundException e) {
                    if (useReferencesKeeper) {
                        // store reference for later
                        for (String path : paths) {
                            JCRNodeWrapper r = refRoot.addNode("j:reference" + UUID.randomUUID().toString(),
                                    "jnt:reference");
                            String refuuid = path.substring(0, path.lastIndexOf("/"));
                            String pName = path.substring(path.lastIndexOf("/") + 1);
                            r.setProperty(Constants.NODE, refuuid);
                            r.setProperty("j:propertyName", pName);
                            r.setProperty("j:originalUuid", uuid);
                            r.setProperty("j:live", keepReferencesForLive);
                        }
                        logger.warn(
                                "Reference to " + uuid + " cannot be resolved, store it in the reference keeper");
                    } else {
                        resolved = false;
                    }
                } catch (RepositoryException e) {
                    logger.error("Repository exception", e);
                }
            }
            if (resolved) {
                resolvedUUIDStringList.add(uuid);
            }
        }
        for (String uuid : resolvedUUIDStringList) {
            references.remove(uuid);
        }

        if (useReferencesKeeper && session != referencesKeeperSession) {
            referencesKeeperSession.save();
        }
    }

    public static JCRSessionWrapper resolveReferencesKeeper(JCRSessionWrapper session) throws RepositoryException {
        NodeIterator ni = null;
        JCRSessionWrapper referencesKeeperSession = null;
        try {
            referencesKeeperSession = getReferencesKeeperSession(session);
            ni = referencesKeeperSession.getNode("/referencesKeeper").getNodes();
        } catch (RepositoryException e) {
            logger.error("Impossible to load the references keeper", e);
            return session;
        }
        if (ni.getSize() > 5000) {
            logger.warn("You have " + ni.getSize()
                    + " nodes under /referencesKeeper, please consider checking the fine-tuning guide to clean them. Parsing them may take a while.");
        }

        int batchCount = 0;
        Map<String, String> uuidMapping = session.getUuidMapping();
        while (ni.hasNext()) {

            batchCount++;

            if (batchCount > MAX_BATCH) {
                referencesKeeperSession.save();
                batchCount = 0;
            }

            Node refNode = ni.nextNode();
            String refuuid = refNode.getProperty(Constants.NODE).getString();

            try {
                JCRNodeWrapper n = session.getNodeByUUID(refuuid);
                String uuid = refNode.getProperty("j:originalUuid").getString();
                if (uuidMapping.containsKey(uuid)) {
                    String pName = refNode.getProperty("j:propertyName").getString();
                    updateProperty(session, n, pName, uuidMapping.get(uuid),
                            refNode.hasProperty("j:live") && refNode.getProperty("j:live").getBoolean());
                    refNode.remove();
                } else if (uuid.startsWith("/") && session.itemExists(uuid)) {
                    String pName = refNode.getProperty("j:propertyName").getString();
                    updateProperty(session, n, pName, session.getNode(uuid).getIdentifier(),
                            refNode.hasProperty("j:live") && refNode.getProperty("j:live").getBoolean());
                    refNode.remove();
                }
            } catch (ItemNotFoundException e) {
                refNode.remove();
            }
        }

        if (session != referencesKeeperSession) {
            referencesKeeperSession.save();
        }

        return referencesKeeperSession;
    }

    private static JCRSessionWrapper getReferencesKeeperSession(JCRSessionWrapper session)
            throws RepositoryException {
        if (session.isSystem()) {
            return session;
        }
        return JCRSessionFactory.getInstance().getCurrentSystemSession(session.getWorkspace().getName(),
                session.getLocale(), session.getFallbackLocale());
    }

    private static void update(List<String> paths, JCRSessionWrapper session, String value)
            throws RepositoryException {
        for (String path : paths) {
            try {
                JCRNodeWrapper n = session.getNodeByUUID(path.substring(0, path.lastIndexOf("/")));
                String pName = path.substring(path.lastIndexOf("/") + 1);
                if (pName.startsWith("@")) {
                    JCRNodeWrapper ref = n.addNode(pName.substring(1), "jnt:contentReference");
                    updateProperty(session, ref, Constants.NODE, value, false);
                } else {
                    try {
                        updateProperty(session, n, pName, value, false);
                    } catch (ItemNotFoundException e) {
                        logger.warn("Item not found: " + pName, e);
                    }
                }
            } catch (RepositoryException e) {
                logger.warn("Error updating reference: " + path, e);
            }
        }
    }

    private static void updateProperty(JCRSessionWrapper session, JCRNodeWrapper n, String pName, String value,
            boolean live) throws RepositoryException {
        if (pName.startsWith("[")) {
            int id = Integer.parseInt(StringUtils.substringBetween(pName, "[", "]"));
            pName = StringUtils.substringAfter(pName, "]");
            if (n.isNodeType("jnt:translation") && n.hasProperty("jcr:language")) {
                pName += "_" + n.getProperty("jcr:language").getString();
                n = n.getParent();
            }
            if (!n.isNodeType("jmix:referencesInField")) {
                n.addMixin("jmix:referencesInField");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("New references : " + value);
            }
            JCRNodeWrapper ref = n.addNode("j:referenceInField_" + Text.escapeIllegalJcrChars(pName) + "_" + id,
                    "jnt:referenceInField");
            ref.setProperty("j:fieldName", pName);
            ref.setProperty("j:reference", value);
        } else if (pName.startsWith("@")) {
            JCRNodeWrapper ref = n.addNode(pName.substring(1), "jnt:contentReference");
            updateProperty(session, ref, Constants.NODE, value, false);
        } else {
            final ExtendedPropertyDefinition propertyDefinition = n.getApplicablePropertyDefinition(pName);
            if (propertyDefinition == null) {
                throw new ConstraintViolationException("Couldn't find definition for property " + pName);
            }
            String[] constraints = propertyDefinition.getValueConstraints();
            if (constraints != null && constraints.length > 0) {
                boolean b = false;
                JCRNodeWrapper target = session.getNodeByUUID(value);
                for (int i = 0; i < constraints.length; i++) {
                    String constraint = constraints[i];
                    b |= target.isNodeType(constraint);
                }
                if (!b) {
                    logger.warn("Cannot set reference to " + target.getPath() + ", constraint on " + n.getPath());
                    return;
                }
            }
            try {
                if (propertyDefinition.isMultiple()) {
                    Value[] newValues;
                    if (n.hasProperty(pName)) {
                        final Value[] oldValues = n.getProperty(pName).getValues();
                        newValues = new Value[oldValues.length + 1];
                        for (Value oldValue : oldValues) {
                            // value already set
                            if (oldValue.getString().equals(value)) {
                                return;
                            }
                        }
                        System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
                    } else {
                        newValues = new Value[1];
                    }
                    newValues[newValues.length - 1] = session.getValueFactory().createValue(value,
                            propertyDefinition.getRequiredType());
                    if (!n.hasProperty(pName) || !Arrays.equals(newValues, n.getProperty(pName).getValues())) {
                        session.checkout(n);
                        JCRPropertyWrapper property = n.setProperty(pName, newValues);

                        if (live) {
                            try {
                                property.getParent().getCorrespondingNodePath("live");
                                String key = property.getParent().getIdentifier() + "/" + property.getName();
                                if (!session.getResolvedReferences().containsKey(key)) {
                                    session.getResolvedReferences().put(key, new HashSet<String>());
                                }
                                ((Set<String>) session.getResolvedReferences().get(key)).add(value);
                            } catch (ItemNotFoundException e) {
                                // Not in live
                            }
                        }
                    }
                } else {
                    if (!n.hasProperty(pName) || !value.equals(n.getProperty(pName).getString())) {
                        session.checkout(n);
                        JCRPropertyWrapper property = n.setProperty(pName,
                                session.getValueFactory().createValue(value, propertyDefinition.getRequiredType()));
                        String key = property.getParent().getIdentifier() + "/" + property.getName();
                        if (live) {
                            try {
                                property.getParent().getCorrespondingNodePath("live");
                                session.getResolvedReferences().put(key, property.getValue().getString());
                            } catch (ItemNotFoundException e) {
                                // Not in live
                            }
                        }
                    }
                }
            } catch (RuntimeException e) {
                String msg = "Error setting property " + pName + " on node " + n.getPath() + " definition "
                        + propertyDefinition + " required type " + propertyDefinition.getRequiredType()
                        + ". Cause: " + e.getMessage();
                if (logger.isDebugEnabled()) {
                    // in debug we log the full exception stacktrace
                    logger.error(msg, e);
                } else {
                    logger.error(msg);
                }
            }
        }
    }

    public static void updateReferencesInLive(final Map<String, Object> resolvedReferences)
            throws RepositoryException {
        if (!resolvedReferences.isEmpty()) {
            JCRTemplate.getInstance().doExecuteWithSystemSessionAsUser(null, "live", null,
                    new JCRCallback<Object>() {

                        public Object doInJCR(JCRSessionWrapper session) throws RepositoryException {
                            for (Map.Entry<String, Object> entry : resolvedReferences.entrySet()) {
                                String nodeIdentifier = StringUtils.substringBeforeLast(entry.getKey(), "/");
                                String propertyName = StringUtils.substringAfterLast(entry.getKey(), "/");

                                try {
                                    JCRNodeWrapper node = session.getNodeByIdentifier(nodeIdentifier);
                                    ExtendedPropertyDefinition definition = node
                                            .getApplicablePropertyDefinition(propertyName);
                                    if (definition != null) {
                                        if (entry.getValue() instanceof String) {
                                            if (!node.hasProperty(propertyName) || !node.getProperty(propertyName)
                                                    .getString().equals(entry.getValue())) {
                                                node.setProperty(propertyName,
                                                        session.getValueFactory().createValue(
                                                                (String) entry.getValue(),
                                                                definition.getRequiredType()));
                                            }
                                        } else if (entry.getValue() instanceof Set) {
                                            Set<String> values = (Set<String>) entry.getValue();
                                            if (!node.hasProperty(propertyName)) {
                                                JCRPropertyWrapper property = node.setProperty(propertyName,
                                                        new Value[0]);
                                                for (String value : values) {
                                                    property.addValue(session.getValueFactory().createValue(value,
                                                            definition.getRequiredType()));
                                                }
                                            } else {
                                                JCRPropertyWrapper property = node.getProperty(propertyName);
                                                Value[] previous = property.getValues();
                                                for (Value value : previous) {
                                                    values.remove(value.getString());
                                                }
                                                for (String value : values) {
                                                    property.addValue(session.getValueFactory().createValue(value,
                                                            definition.getRequiredType()));
                                                }
                                            }
                                        }
                                    }
                                } catch (ItemNotFoundException e) {
                                    logger.debug("Node not found in live", e);
                                }
                            }
                            session.save();
                            return null;
                        }
                    });
            resolvedReferences.clear();
        }
    }

}