org.wso2.carbon.transport.file.connector.server.FileConsumer.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.transport.file.connector.server.FileConsumer.java

Source

/*
 * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you 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.
 */

package org.wso2.carbon.transport.file.connector.server;

import org.apache.commons.vfs2.FileContent;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.FileSystemOptions;
import org.apache.commons.vfs2.FileType;
import org.apache.commons.vfs2.impl.StandardFileSystemManager;
import org.apache.commons.vfs2.provider.UriParser;
import org.apache.commons.vfs2.provider.ftp.FtpFileSystemConfigBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.messaging.CarbonMessage;
import org.wso2.carbon.messaging.CarbonMessageProcessor;
import org.wso2.carbon.messaging.StreamingCarbonMessage;
import org.wso2.carbon.messaging.exceptions.ServerConnectorException;
import org.wso2.carbon.transport.file.connector.server.exception.FileServerConnectorException;
import org.wso2.carbon.transport.file.connector.server.util.Constants;
import org.wso2.carbon.transport.file.connector.server.util.FileTransportUtils;

import java.io.InputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

/**
 * Provides the capability to process a file and delete it afterwards.
 */
public class FileConsumer {

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

    private Map<String, String> fileProperties;
    private FileSystemManager fsManager = null;
    private String serviceName;
    private CarbonMessageProcessor messageProcessor;
    private String fileURI;
    private FileObject fileObject;
    private FileSystemOptions fso;
    /**
     * Time-out interval (in mill-seconds) to wait for the callback.
     */
    private long timeOutInterval = 30000;
    private boolean deleteIfNotAck = false;

    public FileConsumer(String id, Map<String, String> fileProperties, CarbonMessageProcessor messageProcessor)
            throws ServerConnectorException {
        this.serviceName = id;
        this.fileProperties = fileProperties;
        this.messageProcessor = messageProcessor;

        setupParams();
        try {
            StandardFileSystemManager fsm = new StandardFileSystemManager();
            fsm.setConfiguration(getClass().getClassLoader().getResource("providers.xml"));
            fsm.init();
            fsManager = fsm;
        } catch (FileSystemException e) {
            throw new ServerConnectorException(
                    "Could not initialize File System Manager from " + "the configuration: providers.xml", e);
        }
        Map<String, String> options = parseSchemeFileOptions(fileURI);
        fso = FileTransportUtils.attachFileSystemOptions(options, fsManager);

        if (options != null && Constants.SCHEME_FTP.equals(options.get(Constants.SCHEME))) {
            FtpFileSystemConfigBuilder.getInstance().setPassiveMode(fso, true);
        }

        try {
            fileObject = fsManager.resolveFile(fileURI, fso);
        } catch (FileSystemException e) {
            throw new FileServerConnectorException(
                    "Failed to resolve fileURI: " + FileTransportUtils.maskURLPassword(fileURI), e);
        }
    }

    /**
     * Do the file processing operation for the given set of properties. Do the
     * checks and pass the control to processFile method
     */
    public void consume() throws FileServerConnectorException {
        if (log.isDebugEnabled()) {
            log.debug("Polling for directory or file : " + FileTransportUtils.maskURLPassword(fileURI));
        }

        // If file/folder found proceed to the processing stage
        try {
            boolean isFileExists;
            try {
                isFileExists = fileObject.exists();
            } catch (FileSystemException e) {
                throw new FileServerConnectorException("Error occurred when determining whether the file at URI : "
                        + FileTransportUtils.maskURLPassword(fileURI) + " exists. " + e);
            }

            boolean isFileReadable;
            try {
                isFileReadable = fileObject.isReadable();
            } catch (FileSystemException e) {
                throw new FileServerConnectorException("Error occurred when determining whether the file at URI : "
                        + FileTransportUtils.maskURLPassword(fileURI) + " is readable. " + e);
            }

            if (isFileExists && isFileReadable) {
                FileType fileType;
                try {
                    fileType = fileObject.getType();
                } catch (FileSystemException e) {
                    throw new FileServerConnectorException("Error occurred when determining whether file: "
                            + FileTransportUtils.maskURLPassword(fileURI) + " is a file or a folder", e);
                }

                if (fileType == FileType.FILE) {
                    processFile(fileObject);
                    deleteFile(fileObject);
                } else if (fileType == FileType.FOLDER) {
                    FileObject[] children = null;
                    try {
                        children = fileObject.getChildren();
                    } catch (FileSystemException ignored) {
                        if (log.isDebugEnabled()) {
                            log.debug("The file does not exist, or is not a folder, or an error "
                                    + "has occurred when trying to list the children. File URI : "
                                    + FileTransportUtils.maskURLPassword(fileURI), ignored);
                        }
                    }

                    // if this is a file that would translate to a single message
                    if (children == null || children.length == 0) {
                        if (log.isDebugEnabled()) {
                            log.debug("Folder at " + FileTransportUtils.maskURLPassword(fileURI) + " is empty.");
                        }
                    } else {
                        directoryHandler(children);
                    }
                } else {
                    throw new FileServerConnectorException("File: " + FileTransportUtils.maskURLPassword(fileURI)
                            + " is neither a file or " + "a folder"
                            + (fileType == null ? "" : ". Found file type: " + fileType.toString()));
                }
            } else {
                throw new FileServerConnectorException("Unable to access or read file or directory : "
                        + FileTransportUtils.maskURLPassword(fileURI) + ". Reason: "
                        + (isFileExists ? (isFileReadable ? "Unknown reason" : "The file can not be read!")
                                : "The file does not exist!"));
            }
        } finally {
            try {
                fileObject.close();
            } catch (FileSystemException e) {
                log.warn("Could not close file at URI: " + FileTransportUtils.maskURLPassword(fileURI), e);
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("End : Scanning directory or file : " + FileTransportUtils.maskURLPassword(fileURI));
        }
    }

    /**
     * Setup the required transport parameters
     */
    private void setupParams() throws ServerConnectorException {
        fileURI = fileProperties.get(Constants.TRANSPORT_FILE_FILE_URI);
        if (fileURI == null) {
            throw new ServerConnectorException(Constants.TRANSPORT_FILE_FILE_URI + " is a "
                    + "mandatory parameter for " + Constants.PROTOCOL_NAME + " transport.");
        }
        if (fileURI.trim().equals("")) {
            throw new ServerConnectorException(Constants.TRANSPORT_FILE_FILE_URI + " parameter "
                    + "cannot be empty for " + Constants.PROTOCOL_NAME + " transport.");
        }
        String timeOut = fileProperties.get(Constants.FILE_ACKNOWLEDGEMENT_TIME_OUT);
        if (timeOut != null) {
            try {
                timeOutInterval = Long.parseLong(timeOut);
            } catch (NumberFormatException e) {
                log.error("Provided " + Constants.FILE_ACKNOWLEDGEMENT_TIME_OUT + " is invalid. "
                        + "Using the default callback timeout, " + timeOutInterval + " milliseconds", e);
            }
        }
        String strDeleteIfNotAck = fileProperties.get(Constants.FILE_DELETE_IF_NOT_ACKNOWLEDGED);
        if (strDeleteIfNotAck != null) {
            deleteIfNotAck = Boolean.parseBoolean(strDeleteIfNotAck);
        }
    }

    private Map<String, String> parseSchemeFileOptions(String fileURI) {
        String scheme = UriParser.extractScheme(fileURI);
        if (scheme == null) {
            return null;
        }
        HashMap<String, String> schemeFileOptions = new HashMap<>();
        schemeFileOptions.put(Constants.SCHEME, scheme);
        addOptions(scheme, schemeFileOptions);
        return schemeFileOptions;
    }

    private void addOptions(String scheme, Map<String, String> schemeFileOptions) {
        if (scheme.equals(Constants.SCHEME_SFTP)) {
            for (Constants.SftpFileOption option : Constants.SftpFileOption.values()) {
                String strValue = fileProperties.get(Constants.SFTP_PREFIX + option.toString());
                if (strValue != null && !strValue.equals("")) {
                    schemeFileOptions.put(option.toString(), strValue);
                }
            }
        }
    }

    /**
     * Handle directory with chile elements
     *
     * @param children
     * @return
     * @throws FileSystemException
     */
    private void directoryHandler(FileObject[] children) throws FileServerConnectorException {
        // Sort the files
        String strSortParam = fileProperties.get(Constants.FILE_SORT_PARAM);

        if (strSortParam != null && !"NONE".equals(strSortParam)) {
            log.debug("Starting to sort the files in folder: " + FileTransportUtils.maskURLPassword(fileURI));

            String strSortOrder = fileProperties.get(Constants.FILE_SORT_ORDER);
            boolean bSortOrderAscending = true;

            if (strSortOrder != null) {
                bSortOrderAscending = Boolean.parseBoolean(strSortOrder);
            }
            if (log.isDebugEnabled()) {
                log.debug("Sorting the files by : " + strSortOrder + ". (" + bSortOrderAscending + ")");
            }
            switch (strSortParam) {
            case Constants.FILE_SORT_VALUE_NAME:
                if (bSortOrderAscending) {
                    Arrays.sort(children, new FileNameAscComparator());
                } else {
                    Arrays.sort(children, new FileNameDesComparator());
                }
                break;
            case Constants.FILE_SORT_VALUE_SIZE:
                if (bSortOrderAscending) {
                    Arrays.sort(children, new FileSizeAscComparator());
                } else {
                    Arrays.sort(children, new FileSizeDesComparator());
                }
                break;
            case Constants.FILE_SORT_VALUE_LASTMODIFIEDTIMESTAMP:
                if (bSortOrderAscending) {
                    Arrays.sort(children, new FileLastmodifiedtimestampAscComparator());
                } else {
                    Arrays.sort(children, new FileLastmodifiedtimestampDesComparator());
                }
                break;
            default:
                log.warn("Invalid value given for " + Constants.FILE_SORT_PARAM + " parameter. "
                        + " Expected one of the values: " + Constants.FILE_SORT_VALUE_NAME + ", "
                        + Constants.FILE_SORT_VALUE_SIZE + " or " + Constants.FILE_SORT_VALUE_LASTMODIFIEDTIMESTAMP
                        + ". Found: " + strSortParam);
                break;
            }
            if (log.isDebugEnabled()) {
                log.debug("End sorting the files.");
            }
        }
        for (FileObject child : children) {
            processFile(child);
            deleteFile(child);
            //close the file system after processing
            try {
                child.close();
            } catch (FileSystemException e) {
                log.warn("Could not close the file: " + child.getName().getPath(), e);
            }
        }
    }

    /**
     * Actual processing of the file/folder
     *
     * @param file
     * @return
     */
    private FileObject processFile(FileObject file) throws FileServerConnectorException {
        FileContent content;
        String fileURI;

        String fileName = file.getName().getBaseName();
        String filePath = file.getName().getPath();
        fileURI = file.getName().getURI();

        try {
            content = file.getContent();
        } catch (FileSystemException e) {
            throw new FileServerConnectorException(
                    "Could not read content of file at URI: " + FileTransportUtils.maskURLPassword(fileURI) + ". ",
                    e);
        }

        InputStream inputStream;
        try {
            inputStream = content.getInputStream();
        } catch (FileSystemException e) {
            throw new FileServerConnectorException("Error occurred when trying to get "
                    + "input stream from file at URI :" + FileTransportUtils.maskURLPassword(fileURI), e);
        }
        CarbonMessage cMessage = new StreamingCarbonMessage(inputStream);
        cMessage.setProperty(org.wso2.carbon.messaging.Constants.PROTOCOL, Constants.PROTOCOL_NAME);
        cMessage.setProperty(Constants.FILE_TRANSPORT_PROPERTY_SERVICE_NAME, serviceName);
        cMessage.setHeader(Constants.FILE_PATH, filePath);
        cMessage.setHeader(Constants.FILE_NAME, fileName);
        cMessage.setHeader(Constants.FILE_URI, fileURI);
        try {
            cMessage.setHeader(Constants.FILE_LENGTH, Long.toString(content.getSize()));
            cMessage.setHeader(Constants.LAST_MODIFIED, Long.toString(content.getLastModifiedTime()));
        } catch (FileSystemException e) {
            log.warn("Unable to set file length or last modified date header.", e);
        }

        FileServerConnectorCallback callback = new FileServerConnectorCallback();
        try {
            messageProcessor.receive(cMessage, callback);
        } catch (Exception e) {
            throw new FileServerConnectorException("Failed to send stream from file: "
                    + FileTransportUtils.maskURLPassword(fileURI) + " to message processor. ", e);
        }
        try {
            callback.waitTillDone(timeOutInterval, deleteIfNotAck, fileURI);
        } catch (InterruptedException e) {
            throw new FileServerConnectorException("Interrupted while waiting for message "
                    + "processor to consume the file input stream. Aborting processing of file: "
                    + FileTransportUtils.maskURLPassword(fileURI), e);
        }
        return file;
    }

    /**
     * Do the post processing actions
     *
     * @param fileObject
     */
    private void deleteFile(FileObject fileObject) throws FileServerConnectorException {
        if (log.isDebugEnabled()) {
            log.debug("Deleting file :" + FileTransportUtils.maskURLPassword(fileObject.getName().getBaseName()));
        }
        try {
            if (!fileObject.delete()) {
                throw new FileServerConnectorException("Could not delete file : "
                        + FileTransportUtils.maskURLPassword(fileObject.getName().getBaseName()));
            }
        } catch (FileSystemException e) {
            throw new FileServerConnectorException("Could not delete file : "
                    + FileTransportUtils.maskURLPassword(fileObject.getName().getBaseName()), e);
        }
    }

    /**
     * Comparator classed used to sort the files according to user input
     */
    static class FileNameAscComparator implements Comparator<FileObject>, Serializable {

        private static final long serialVersionUID = 1;

        @Override
        public int compare(FileObject o1, FileObject o2) {
            return o1.getName().compareTo(o2.getName());
        }
    }

    static class FileLastmodifiedtimestampAscComparator implements Comparator<FileObject>, Serializable {

        private static final long serialVersionUID = 1;

        @Override
        public int compare(FileObject o1, FileObject o2) {
            Long lDiff = 0L;
            try {
                lDiff = o1.getContent().getLastModifiedTime() - o2.getContent().getLastModifiedTime();
            } catch (FileSystemException e) {
                log.warn("Unable to compare last modified timestamp of the two files.", e);
            }
            return lDiff.intValue();
        }
    }

    static class FileSizeAscComparator implements Comparator<FileObject>, Serializable {

        private static final long serialVersionUID = 1;

        @Override
        public int compare(FileObject o1, FileObject o2) {
            Long lDiff = 0L;
            try {
                lDiff = o1.getContent().getSize() - o2.getContent().getSize();
            } catch (FileSystemException e) {
                log.warn("Unable to compare size of the two files.", e);
            }
            return lDiff.intValue();
        }
    }

    static class FileNameDesComparator implements Comparator<FileObject>, Serializable {

        private static final long serialVersionUID = 1;

        @Override
        public int compare(FileObject o1, FileObject o2) {
            return o2.getName().compareTo(o1.getName());
        }
    }

    static class FileLastmodifiedtimestampDesComparator implements Comparator<FileObject>, Serializable {

        private static final long serialVersionUID = 1;

        @Override
        public int compare(FileObject o1, FileObject o2) {
            Long lDiff = 0L;
            try {
                lDiff = o2.getContent().getLastModifiedTime() - o1.getContent().getLastModifiedTime();
            } catch (FileSystemException e) {
                log.warn("Unable to compare last modified timestamp of the two files.", e);
            }
            return lDiff.intValue();
        }
    }

    static class FileSizeDesComparator implements Comparator<FileObject>, Serializable {

        private static final long serialVersionUID = 1;

        @Override
        public int compare(FileObject o1, FileObject o2) {
            Long lDiff = 0L;
            try {
                lDiff = o2.getContent().getSize() - o1.getContent().getSize();
            } catch (FileSystemException e) {
                log.warn("Unable to compare size of the two files.", e);
            }
            return lDiff.intValue();
        }
    }

}