org.jahia.modules.external.vfs.VFSDataSource.java Source code

Java tutorial

Introduction

Here is the source code for org.jahia.modules.external.vfs.VFSDataSource.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.vfs;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.vfs2.*;
import org.apache.jackrabbit.util.ISO8601;
import org.jahia.api.Constants;
import org.jahia.modules.external.ExternalData;
import org.jahia.modules.external.ExternalDataSource;
import org.jahia.services.content.JCRContentUtils;
import org.jahia.services.content.nodetypes.ExtendedNodeType;
import org.jahia.services.content.nodetypes.NodeTypeRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Binary;
import javax.jcr.ItemNotFoundException;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;

/**
 * VFS Implementation of ExternalDataSource
 */
public class VFSDataSource
        implements ExternalDataSource, ExternalDataSource.Writable, ExternalDataSource.CanLoadChildrenInBatch {
    private static final List<String> JCR_CONTENT_LIST = Arrays.asList(Constants.JCR_CONTENT);
    private static final Set<String> SUPPORTED_NODE_TYPES = new HashSet<String>(
            Arrays.asList(Constants.JAHIANT_FILE, Constants.JAHIANT_FOLDER, Constants.JCR_CONTENT));
    private static final Logger logger = LoggerFactory.getLogger(VFSDataSource.class);
    private static final String JCR_CONTENT_SUFFIX = "/" + Constants.JCR_CONTENT;
    private FileObject root;
    private String rootPath;
    private FileSystemManager manager;

    /**
     * Defines the root point of the DataSource
     *
     * @param rootUri
     */
    public void setRoot(String rootUri) {
        try {
            manager = VFS.getManager();
            root = manager.resolveFile(rootUri);
            rootPath = root.getName().getPath();
        } catch (Exception e) {
            throw new RuntimeException("Cannot set root to " + rootUri, e);
        }
    }

    protected FileObject getRoot() {
        return root;
    }

    protected String getRootPath() {
        return rootPath;
    }

    protected FileSystemManager getManager() {
        return manager;
    }

    public boolean isSupportsUuid() {
        return false;
    }

    @Override
    public boolean isSupportsHierarchicalIdentifiers() {
        return true;
    }

    @Override
    public boolean itemExists(String path) {
        try {
            FileObject file = getFile(
                    path.endsWith(JCR_CONTENT_SUFFIX) ? StringUtils.substringBeforeLast(path, JCR_CONTENT_SUFFIX)
                            : path);
            return file.exists();
        } catch (FileSystemException e) {
            logger.warn("Unable to check file existence for path " + path, e);
        }
        return false;
    }

    @Override
    public void order(String path, List<String> children) throws RepositoryException {
        // ordering is not supported in VFS
    }

    public Set<String> getSupportedNodeTypes() {
        return SUPPORTED_NODE_TYPES;
    }

    public ExternalData getItemByIdentifier(String identifier) throws ItemNotFoundException {
        if (identifier.startsWith("/")) {
            try {
                return getItemByPath(identifier);
            } catch (PathNotFoundException e) {
                throw new ItemNotFoundException(identifier, e);
            }
        }
        throw new ItemNotFoundException(identifier);
    }

    public ExternalData getItemByPath(String path) throws PathNotFoundException {
        try {
            if (path.endsWith(JCR_CONTENT_SUFFIX)) {
                FileObject fileObject = getFile(StringUtils.substringBeforeLast(path, JCR_CONTENT_SUFFIX));
                FileContent content = fileObject.getContent();
                if (!fileObject.exists()) {
                    throw new PathNotFoundException(path);
                }
                return getFileContent(content);
            } else {
                FileObject fileObject = getFile(path);
                if (!fileObject.exists()) {
                    throw new PathNotFoundException(path);
                }
                return getFile(fileObject);
            }

        } catch (FileSystemException e) {
            throw new PathNotFoundException("File system exception while trying to retrieve " + path, e);
        }
    }

    public FileObject getFile(String path) throws FileSystemException {
        return (path == null || path.length() == 0 || path.equals("/")) ? root
                : root.resolveFile(path.charAt(0) == '/' ? path.substring(1) : path);
    }

    public List<String> getChildren(String path) throws RepositoryException {
        try {
            if (!path.endsWith(JCR_CONTENT_SUFFIX)) {
                FileObject fileObject = getFile(path);
                if (fileObject.getType() == FileType.FILE) {
                    return JCR_CONTENT_LIST;
                } else if (fileObject.getType() == FileType.FOLDER) {
                    FileObject[] files = fileObject.getChildren();
                    if (files.length > 0) {
                        List<String> children = new LinkedList<String>();
                        for (FileObject object : files) {
                            if (getSupportedNodeTypes().contains(getDataType(object))) {
                                children.add(object.getName().getBaseName());
                            }
                        }
                        return children;
                    } else {
                        return Collections.emptyList();
                    }
                } else {
                    if (fileObject.exists()) {
                        logger.warn("Found non file or folder entry at path {}, maybe an alias. VFS file type: {}",
                                fileObject, fileObject.getType());
                    } else {
                        throw new PathNotFoundException(path);
                    }
                }
            }
        } catch (FileSystemException e) {
            logger.error("Cannot get node children", e);
        }

        return Collections.emptyList();
    }

    @Override
    public List<ExternalData> getChildrenNodes(String path) throws RepositoryException {
        try {
            if (!path.endsWith(JCR_CONTENT_SUFFIX)) {
                FileObject fileObject = getFile(path);
                if (fileObject.getType() == FileType.FILE) {
                    final FileContent content = fileObject.getContent();
                    return Collections.singletonList(getFileContent(content));
                } else if (fileObject.getType() == FileType.FOLDER) {
                    fileObject.refresh(); //in case of folder, refresh because it could be changed external               
                    FileObject[] files = fileObject.getChildren();
                    if (files.length > 0) {
                        List<ExternalData> children = new LinkedList<ExternalData>();
                        for (FileObject object : files) {
                            if (getSupportedNodeTypes().contains(getDataType(object))) {
                                children.add(getFile(object));
                                if (object.getType() == FileType.FILE) {
                                    children.add(getFileContent(object.getContent()));
                                }
                            }
                        }
                        return children;
                    } else {
                        return Collections.emptyList();
                    }
                } else {
                    if (fileObject.exists()) {
                        logger.warn("Found non file or folder entry at path {}, maybe an alias. VFS file type: {}",
                                fileObject, fileObject.getType());
                    } else {
                        throw new PathNotFoundException(path);
                    }
                }
            }
        } catch (FileSystemException e) {
            logger.error("Cannot get node children", e);
        }

        return Collections.emptyList();
    }

    @Override
    public void removeItemByPath(String path) throws RepositoryException {
        try {
            FileObject file = getFile(path);
            if (file.getType().hasChildren()) {
                file.delete(Selectors.SELECT_ALL);
            } else if (!file.delete()) {
                logger.warn("Failed to delete FileObject {}", getFile(path).toString());
            }
        } catch (FileSystemException e) {
            throw new RepositoryException(e);
        }
    }

    public void saveItem(ExternalData data) throws RepositoryException {
        try {
            ExtendedNodeType nodeType = NodeTypeRegistry.getInstance().getNodeType(data.getType());
            if (nodeType.isNodeType(Constants.NT_RESOURCE)
                    && StringUtils.contains(data.getPath(), Constants.JCR_CONTENT)) {
                OutputStream outputStream = null;
                try {
                    final Binary[] binaries = data.getBinaryProperties().get(Constants.JCR_DATA);
                    if (binaries.length > 0) {
                        outputStream = getFile(
                                data.getPath().substring(0, data.getPath().indexOf(JCR_CONTENT_SUFFIX)))
                                        .getContent().getOutputStream();
                        for (Binary binary : binaries) {
                            InputStream stream = null;
                            try {
                                stream = binary.getStream();
                                IOUtils.copy(stream, outputStream);
                            } finally {
                                IOUtils.closeQuietly(stream);
                            }
                        }
                    }
                } catch (IOException e) {
                    throw new PathNotFoundException("I/O on file : " + data.getPath(), e);
                } catch (RepositoryException e) {
                    throw new PathNotFoundException("unable to get outputStream of : " + data.getPath(), e);
                } finally {
                    IOUtils.closeQuietly(outputStream);
                }
            } else if (nodeType.isNodeType("jnt:folder")) {
                try {
                    getFile(data.getPath()).createFolder();
                } catch (FileSystemException e) {
                    throw new PathNotFoundException(data.getPath(), e);
                }
            }
        } catch (NoSuchNodeTypeException e) {
            throw new PathNotFoundException(e);
        }
    }

    @Override
    public void move(String oldPath, String newPath) throws RepositoryException {
        if (oldPath.equals(newPath)) {
            return;
        }
        try {
            FileObject origin = getFile(oldPath);
            if (origin.isContentOpen()) {
                origin.close();
            }
            FileObject destination = getFile(newPath);
            if (destination.exists() && destination.isContentOpen()) {
                destination.close();
            }
            origin.moveTo(destination);
        } catch (FileSystemException e) {
            throw new RepositoryException(oldPath, e);
        }
    }

    private ExternalData getFile(FileObject fileObject) throws FileSystemException {
        String type = getDataType(fileObject);

        Map<String, String[]> properties = new HashMap<String, String[]>();
        List<String> addedMixins = new ArrayList<>();
        final FileContent content = fileObject.getContent();
        if (content != null) {
            long lastModifiedTime = fileObject.getContent().getLastModifiedTime();
            if (lastModifiedTime > 0) {
                Calendar calendar = Calendar.getInstance();
                calendar.setTimeInMillis(lastModifiedTime);
                String[] timestamp = new String[] { ISO8601.format(calendar) };
                properties.put(Constants.JCR_CREATED, timestamp);
                properties.put(Constants.JCR_LASTMODIFIED, timestamp);
            }
            // Add jmix:image mixin in case of the file is a picture.
            if (content.getContentInfo() != null && content.getContentInfo().getContentType() != null
                    && fileObject.getContent().getContentInfo().getContentType().matches("image/(.*)")) {
                addedMixins.add(Constants.JAHIAMIX_IMAGE);
            }

        }

        String path = fileObject.getName().getPath().substring(rootPath.length());
        if (!path.startsWith("/")) {
            path = "/" + path;
        }

        ExternalData result = new ExternalData(path, path, type, properties);
        result.setMixin(addedMixins);
        return result;
    }

    public String getDataType(FileObject fileObject) throws FileSystemException {
        return fileObject.getType() == FileType.FILE ? Constants.JAHIANT_FILE : Constants.JAHIANT_FOLDER;
    }

    protected ExternalData getFileContent(final FileContent content) throws FileSystemException {
        Map<String, String[]> properties = new HashMap<String, String[]>(1);

        properties.put(Constants.JCR_MIMETYPE, new String[] { getContentType(content) });

        String path = content.getFile().getName().getPath().substring(rootPath.length());
        String jcrContentPath = path + "/" + Constants.JCR_CONTENT;
        ExternalData externalData = new ExternalData(jcrContentPath, jcrContentPath, Constants.JAHIANT_RESOURCE,
                properties);

        Map<String, Binary[]> binaryProperties = new HashMap<String, Binary[]>(1);
        binaryProperties.put(Constants.JCR_DATA, new Binary[] { new VFSBinaryImpl(content) });
        externalData.setBinaryProperties(binaryProperties);

        return externalData;
    }

    protected String getContentType(FileContent content) throws FileSystemException {
        String s1 = content.getContentInfo().getContentType();
        if (s1 == null) {
            s1 = JCRContentUtils.getMimeType(content.getFile().getName().getBaseName());
        }
        if (s1 == null) {
            s1 = "application/octet-stream";
        }
        return s1;
    }
}