org.jahia.modules.external.ExternalContentStoreProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.jahia.modules.external.ExternalContentStoreProvider.java

Source

/**
 * ==========================================================================================
 * =                   JAHIA'S DUAL LICENSING - IMPORTANT INFORMATION                       =
 * ==========================================================================================
 *
 *                                 http://www.jahia.com
 *
 *     Copyright (C) 2002-2016 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.modules.external;

import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.commons.iterator.PropertyIteratorAdapter;
import org.jahia.api.Constants;
import org.jahia.exceptions.JahiaInitializationException;
import org.jahia.exceptions.JahiaRuntimeException;
import org.jahia.services.content.*;
import org.jahia.services.content.nodetypes.Name;
import org.jahia.services.content.nodetypes.NodeTypeRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

import javax.jcr.*;
import javax.jcr.query.QueryManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Implementation of the {@link org.jahia.services.content.JCRStoreProvider} for the {@link org.jahia.modules.external.ExternalData}.
 *
 * @author Thomas Draier
 */
public class ExternalContentStoreProvider extends JCRStoreProvider implements InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(ExternalContentStoreProvider.class);

    private static final ThreadLocal<ExternalSessionImpl> currentSession = new ThreadLocal<ExternalSessionImpl>();

    private boolean readOnly;

    private ExternalDataSource dataSource;

    private String id;

    private ExternalProviderInitializerService externalProviderInitializerService;

    private List<String> extendableTypes;
    private List<String> nonExtendableMixins;
    private List<String> overridableItems;
    private List<String> nonOverridableItems;
    private List<String> reservedNodes = Arrays.asList("j:acl", "j:workflowRules", "j:conditionalVisibility",
            "thumbnail");

    private List<String> ignorePropertiesForExport = Arrays.asList("j:extendedType", "j:isExternalProviderRoot",
            "j:externalNodeIdentifier");

    private boolean slowConnection = true;
    private boolean lockSupport = false;
    private boolean cacheKeyOnReferenceSupport = false;
    private boolean aclSupport = true;

    public static ExternalSessionImpl getCurrentSession() {
        return currentSession.get();
    }

    public static void setCurrentSession(ExternalSessionImpl session) {
        currentSession.set(session);
    }

    public static void removeCurrentSession() {
        currentSession.remove();
    }

    @Override
    protected Repository createRepository() {
        JCRStoreProvider defaultProvider = JCRSessionFactory.getInstance().getDefaultProvider();
        NamespaceRegistry namespaceRegistry;
        JCRSessionWrapper systemSession = null;
        try {
            systemSession = defaultProvider.getSystemSession();
            namespaceRegistry = systemSession.getProviderSession(defaultProvider).getWorkspace()
                    .getNamespaceRegistry();
        } catch (RepositoryException e) {
            throw new JahiaRuntimeException(e);
        } finally {
            if (systemSession != null) {
                systemSession.logout();
            }
        }
        ExternalRepositoryImpl instance = new ExternalRepositoryImpl(this, dataSource, namespaceRegistry);
        instance.setProviderKey(getKey());

        return instance;
    }

    @Override
    public boolean start(boolean checkAvailability) throws JahiaInitializationException {

        // Enable acl
        if (aclSupport) {
            if (overridableItems != null) {
                List<String> l = new ArrayList<>();
                l.addAll(overridableItems);
                l.addAll(externalProviderInitializerService.getOverridableItemsForACLs());
                overridableItems = l;
            } else {
                overridableItems = new ArrayList<>(externalProviderInitializerService.getOverridableItemsForACLs());
            }
        }

        // Enable lock
        if (lockSupport) {
            if (overridableItems != null) {
                List<String> l = new ArrayList<>();
                l.addAll(overridableItems);
                l.addAll(externalProviderInitializerService.getOverridableItemsForLocks());
                overridableItems = l;
            } else {
                overridableItems = new ArrayList<>(
                        externalProviderInitializerService.getOverridableItemsForLocks());
            }
        }

        getId(); // initialize ID
        if (dataSource instanceof ExternalDataSource.Initializable) {
            ((ExternalDataSource.Initializable) dataSource).start();
        }
        return super.start(checkAvailability);
    }

    @Override
    public void stop() {
        super.stop();
        if (dataSource instanceof ExternalDataSource.Initializable) {
            ((ExternalDataSource.Initializable) dataSource).stop();
        }
    }

    @Override
    protected void initObservers() throws RepositoryException {
        // do nothing
    }

    @Override
    public QueryManager getQueryManager(JCRSessionWrapper session) throws RepositoryException {
        return dataSource instanceof ExternalDataSource.Searchable ? super.getQueryManager(session) : null;
    }

    public ExternalDataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(ExternalDataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (getKey() != null) {
            init();
        }
    }

    /**
     * Initializes this provider instance. Should be called after the {@link #setKey(String)} was called to set the provider key.
     *
     * @throws RepositoryException
     *             in case of an initialization error
     */
    protected void init() throws RepositoryException {
        if (getKey() == null) {
            throw new IllegalArgumentException(
                    "The key is not specified for the provider instance." + " Unable to initialize this provider.");
        }
        id = StringUtils.leftPad(externalProviderInitializerService.getProviderId(getKey()).toString(), 8, "f");
    }

    /**
     * The internal ID of this provider which is also used as a prefix for UUIDs, handled by this provider.
     *
     * @return internal ID of this provider which is also used as a prefix for UUIDs, handled by this provider.
     */
    public String getId() {
        if (id == null) {
            try {
                init();
            } catch (RepositoryException e) {
                throw new JahiaRuntimeException(e.getMessage(), e);
            }
        }
        return id;
    }

    public ExternalProviderInitializerService getExternalProviderInitializerService() {
        return externalProviderInitializerService;
    }

    public void setExternalProviderInitializerService(ExternalProviderInitializerService mappingService) {
        this.externalProviderInitializerService = mappingService;
    }

    public JCRStoreProvider getExtensionProvider() {
        return extendableTypes != null || overridableItems != null
                ? externalProviderInitializerService.getExtensionProvider()
                : null;
    }

    public List<String> getExtendableTypes() {
        return extendableTypes;
    }

    public void setExtendableTypes(List<String> extendableTypes) {
        this.extendableTypes = extendableTypes;
    }

    public List<String> getOverridableItems() {
        return overridableItems;
    }

    public void setOverridableItems(List<String> overridableItems) {
        this.overridableItems = overridableItems;
    }

    public List<String> getNonOverridableItems() {
        return nonOverridableItems;
    }

    public void setNonOverridableItems(List<String> nonOverridableItems) {
        this.nonOverridableItems = nonOverridableItems;
    }

    public List<String> getNonExtendableMixins() {
        return nonExtendableMixins;
    }

    public void setNonExtendableMixins(List<String> nonExtendableMixins) {
        this.nonExtendableMixins = nonExtendableMixins;
    }

    public void setLockSupport(boolean lockSupport) {
        this.lockSupport = lockSupport;
    }

    public void setAclSupport(boolean aclSupport) {
        this.aclSupport = aclSupport;
    }

    public boolean isSlowConnection() {
        return slowConnection;
    }

    public void setSlowConnection(boolean slowConnection) {
        this.slowConnection = slowConnection;
    }

    public List<String> getReservedNodes() {
        return reservedNodes;
    }

    public void setReservedNodes(List<String> reservedNodes) {
        this.reservedNodes = reservedNodes;
    }

    /**
     * Reads internal UUID of the specified node via mapping table, using external ID and provider key.
     *
     * @param externalId
     *            the external ID to retrieve UUID for
     * @return an internal UUID of the specified node via mapping table or <code>null</code> if the mapping is not stored yet
     * @throws RepositoryException
     *             in case an internal identifier cannot be retrieved from the database
     */
    protected String getInternalIdentifier(String externalId) throws RepositoryException {
        return getExternalProviderInitializerService().getInternalIdentifier(externalId, getKey());
    }

    /**
     * Generates the internal UUID for the specified node in the mapping table, using external ID and this provider.
     *
     * @param externalId
     *            the external ID to generate UUID for
     * @return a generated internal UUID
     * @throws RepositoryException
     *             in case an internal identifier cannot be stored into the database
     */
    protected String mapInternalIdentifier(String externalId) throws RepositoryException {
        return getExternalProviderInitializerService().mapInternalIdentifier(externalId, getKey(), getId());
    }

    /**
     * Get internal UUID of the specified node or generate a new if it doesn't exist
     *
     * @param externalId
     *            the external ID to generate UUID for
     * @return an internal UUID
     * @throws RepositoryException
     *             in case an internal identifier cannot be stored into the database
     */
    public String getOrCreateInternalIdentifier(String externalId) throws RepositoryException {
        String internalId = getInternalIdentifier(externalId);
        if (internalId == null) {
            // not mapped yet -> store mapping
            internalId = mapInternalIdentifier(externalId);
        }
        return internalId;
    }

    public PropertyIterator getWeakReferences(JCRNodeWrapper node, String propertyName, Session session)
            throws RepositoryException {
        if (dataSource instanceof ExternalDataSource.Referenceable && session instanceof ExternalSessionImpl) {
            String identifier = node.getIdentifier();
            if (this.equals(node.getProvider())) {
                identifier = ((ExternalNodeImpl) node.getRealNode()).getData().getId();
            }
            setCurrentSession((ExternalSessionImpl) session);
            List<String> referringProperties = null;
            try {
                referringProperties = ((ExternalDataSource.Referenceable) dataSource)
                        .getReferringProperties(identifier, propertyName);
            } finally {
                ExternalContentStoreProvider.removeCurrentSession();
            }
            if (referringProperties == null) {
                return null;
            }
            if (referringProperties.isEmpty()) {
                return PropertyIteratorAdapter.EMPTY;
            }
            List<Property> l = new ArrayList<Property>();
            for (String propertyPath : referringProperties) {
                String nodePath = StringUtils.substringBeforeLast(propertyPath, "/");
                if (nodePath.isEmpty()) {
                    nodePath = "/";
                }
                ExternalNodeImpl referringNode = (ExternalNodeImpl) session.getNode(nodePath);
                if (referringNode != null) {
                    l.add(new ExternalPropertyImpl(
                            new Name(StringUtils.substringAfterLast(propertyPath, "/"),
                                    NodeTypeRegistry.getInstance().getNamespaces()),
                            referringNode, (ExternalSessionImpl) session));
                }
            }
            return new PropertyIteratorAdapter(l);
        }
        return null;
    }

    @Override
    public boolean canExportNode(Node node) {
        try {
            if (node instanceof JCRNodeWrapper) {
                node = ((JCRNodeWrapper) node).getRealNode();
            }
            return Constants.EDIT_WORKSPACE.equals(node.getSession().getWorkspace().getName())
                    && (node instanceof ExtensionNode || (node instanceof ExternalNodeImpl
                            && ((ExternalNodeImpl) node).getExtensionNode(false) != null));
        } catch (RepositoryException e) {
            logger.error("Error while checking if an extension node exists", e);
        }
        return false;
    }

    @Override
    public boolean canExportProperty(Property property) {
        try {
            if (property instanceof JCRPropertyWrapper) {
                property = ((JCRNodeWrapper) property.getParent()).getRealNode().getProperty(property.getName());
            }
            return Constants.EDIT_WORKSPACE.equals(property.getSession().getWorkspace().getName())
                    && ("jcr:primaryType".equals(property.getName()) || "jcr:mixinTypes".equals(property.getName())
                            || (property instanceof ExtensionProperty
                                    && !ignorePropertiesForExport.contains(property.getName())));
        } catch (RepositoryException e) {
            logger.error("Error while checking property name", e);
        }
        return false;
    }

    /**
     * if true,if a content from the provider is referenced, it will be added to the cache key
     * For example it is used in the Alfresco CMIS provider as a content may not be available for all users
     * by default is false
     * @return
     */
    public boolean isCacheKeyOnReferenceSupport() {
        return cacheKeyOnReferenceSupport;
    }

    public void setCacheKeyOnReferenceSupport(boolean cacheKeyOnReferenceSupport) {
        this.cacheKeyOnReferenceSupport = cacheKeyOnReferenceSupport;
    }
}