com.mirth.connect.connectors.file.FileMessageReceiver.java Source code

Java tutorial

Introduction

Here is the source code for com.mirth.connect.connectors.file.FileMessageReceiver.java

Source

/*
 * Copyright (c) Mirth Corporation. All rights reserved.
 * http://www.mirthcorp.com
 *
 * The software in this package is published under the terms of the MPL
 * license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 */

package com.mirth.connect.connectors.file;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.mule.MuleException;
import org.mule.config.i18n.Message;
import org.mule.config.i18n.Messages;
import org.mule.impl.MuleMessage;
import org.mule.providers.PollingMessageReceiver;
import org.mule.providers.TemplateValueReplacer;
import org.mule.providers.VariableFilenameParser;
import org.mule.umo.MessagingException;
import org.mule.umo.UMOComponent;
import org.mule.umo.UMOException;
import org.mule.umo.UMOMessage;
import org.mule.umo.endpoint.UMOEndpoint;
import org.mule.umo.endpoint.UMOEndpointURI;
import org.mule.umo.lifecycle.InitialisationException;
import org.mule.umo.provider.UMOConnector;
import org.mule.umo.provider.UMOMessageAdapter;
import org.mule.umo.routing.RoutingException;

import com.mirth.connect.connectors.file.filesystems.FileInfo;
import com.mirth.connect.connectors.file.filesystems.FileSystemConnection;
import com.mirth.connect.model.MessageObject.Protocol;
import com.mirth.connect.server.Constants;
import com.mirth.connect.server.controllers.AlertController;
import com.mirth.connect.server.controllers.ControllerFactory;
import com.mirth.connect.server.controllers.MonitoringController;
import com.mirth.connect.server.controllers.MonitoringController.ConnectorType;
import com.mirth.connect.server.controllers.MonitoringController.Event;
import com.mirth.connect.server.mule.adaptors.Adaptor;
import com.mirth.connect.server.mule.adaptors.AdaptorFactory;
import com.mirth.connect.server.mule.adaptors.BatchAdaptor;
import com.mirth.connect.server.mule.adaptors.BatchMessageProcessor;
import com.mirth.connect.server.mule.transformers.JavaScriptPostprocessor;

public class FileMessageReceiver extends PollingMessageReceiver implements BatchMessageProcessor {
    private String readDir = null;
    private String moveDir = null;
    private String errorDir = null;
    private String moveToPattern = null;
    private String filenamePattern = null;
    private boolean routingError = false;

    private AlertController alertController = ControllerFactory.getFactory().createAlertController();
    private MonitoringController monitoringController = ControllerFactory.getFactory().createMonitoringController();
    private JavaScriptPostprocessor postProcessor = new JavaScriptPostprocessor();
    private TemplateValueReplacer replacer = new TemplateValueReplacer();
    private ConnectorType connectorType = ConnectorType.READER;
    private FileConnector fileConnector = null;

    private String originalFilename = null;

    public FileMessageReceiver(UMOConnector connector, UMOComponent component, UMOEndpoint endpoint, String readDir,
            String moveDir, String moveToPattern, String errorDir, Long frequency) throws InitialisationException {
        super(connector, component, endpoint, frequency);
        fileConnector = (FileConnector) connector;

        // Replace variables in the readDir now, all others will be done every message
        this.readDir = replacer.replaceValues(readDir, fileConnector.getChannelId());
        this.moveDir = moveDir;
        this.moveToPattern = moveToPattern;
        this.errorDir = errorDir;

        if (fileConnector.getPollingType().equals(FileConnector.POLLING_TYPE_TIME)) {
            setTime(fileConnector.getPollingTime());
        } else {
            setFrequency(fileConnector.getPollingFrequency());
        }

        filenamePattern = replacer.replaceValues(fileConnector.getFileFilter(), fileConnector.getChannelId());
        monitoringController.updateStatus(connector, connectorType, Event.INITIALIZED);
    }

    public void doConnect() throws Exception {
        FileSystemConnection con = fileConnector.getConnection(getEndpointURI(), null);
        fileConnector.releaseConnection(getEndpointURI(), con, null);
    }

    public void doDisconnect() throws Exception {
    }

    public void poll() {

        monitoringController.updateStatus(connector, connectorType, Event.CONNECTED);
        try {

            FileInfo[] files = listFiles();

            if (files == null) {
                return;
            }

            // sort files by specified attribute before processing
            sortFiles(files);
            routingError = false;

            for (int i = 0; i < files.length; i++) {
                //
                if (!routingError && !files[i].isDirectory()) {
                    monitoringController.updateStatus(connector, connectorType, Event.BUSY);
                    processFile(files[i]);
                    monitoringController.updateStatus(connector, connectorType, Event.DONE);
                }
            }
        } catch (Throwable t) {
            alertController.sendAlerts(((FileConnector) connector).getChannelId(), Constants.ERROR_403, null, t);
            handleException(new Exception(t));
        } finally {
            monitoringController.updateStatus(connector, connectorType, Event.DONE);
        }
    }

    public void sortFiles(FileInfo[] files) {
        String sortAttribute = ((FileConnector) connector).getSortAttribute();

        if (sortAttribute.equals(FileConnector.SORT_DATE)) {
            Arrays.sort(files, new Comparator<FileInfo>() {
                public int compare(FileInfo file1, FileInfo file2) {
                    return Float.compare(file1.getLastModified(), file2.getLastModified());
                }
            });
        } else if (sortAttribute.equals(FileConnector.SORT_SIZE)) {
            Arrays.sort(files, new Comparator<FileInfo>() {
                public int compare(FileInfo file1, FileInfo file2) {
                    return Float.compare(file1.getSize(), file2.getSize());
                }
            });
        } else {
            Arrays.sort(files, new Comparator<FileInfo>() {
                public int compare(FileInfo file1, FileInfo file2) {
                    return file1.getName().compareTo(file2.getName());
                }
            });
        }
    }

    /**
     * Converts the supplied message into a MuleMessage and routes it.
     * 
     * @param message
     *            The message to be converted and routed.
     * @throws MessagingException
     *             , UMOException
     */
    public void processBatchMessage(String message) throws MessagingException, UMOException {
        UMOMessageAdapter messageAdapter = connector.getMessageAdapter(message);
        messageAdapter.setProperty(FileConnector.PROPERTY_ORIGINAL_FILENAME, originalFilename);
        UMOMessage umoMessage = routeMessage(new MuleMessage(messageAdapter), endpoint.isSynchronous());
        if (umoMessage != null) {
            postProcessor.doPostProcess(umoMessage.getPayload());
        }
    }

    public synchronized void processFile(FileInfo file) throws UMOException {

        boolean checkFileAge = fileConnector.isCheckFileAge();
        if (checkFileAge) {
            long fileAge = fileConnector.getFileAge();
            long lastMod = file.getLastModified();
            long now = System.currentTimeMillis();
            if ((now - lastMod) < fileAge)
                return;
        }

        String destinationDir = null;
        String destinationName = null;
        originalFilename = file.getName();
        UMOMessageAdapter adapter = connector.getMessageAdapter(file);
        adapter.setProperty(FileConnector.PROPERTY_ORIGINAL_FILENAME, originalFilename);

        if (moveDir != null) {
            destinationName = file.getName();

            VariableFilenameParser filenameParser = (VariableFilenameParser) fileConnector.getFilenameParser();
            filenameParser.setChannelId(fileConnector.getChannelId());

            if (moveToPattern != null) {
                destinationName = filenameParser.getFilename(adapter, moveToPattern);
            }

            destinationDir = filenameParser.getFilename(adapter, moveDir);
        }

        boolean resultOfFileMoveOperation = false;

        try {
            // Perform some quick checks to make sure file can be processed
            if (file.isDirectory()) {
                // ignore directories
            } else if (!(file.isReadable() && file.isFile())) {
                // it's either not readable, or something odd like a link */
                throw new MuleException(new Message(Messages.FILE_X_DOES_NOT_EXIST, file.getName()));
            } else {

                Exception fileProcessedException = null;

                try {

                    // ast: use the user-selected encoding
                    if (fileConnector.isProcessBatchFiles()) {
                        processBatch(file);
                    } else {
                        String message = "";
                        if (fileConnector.isBinary()) {
                            message = new String(Base64.encodeBase64Chunked(getBytesFromFile(file)));
                        } else {
                            message = new String(getBytesFromFile(file), fileConnector.getCharsetEncoding());
                        }
                        adapter = connector.getMessageAdapter(message);
                        adapter.setProperty(FileConnector.PROPERTY_ORIGINAL_FILENAME, originalFilename);
                        UMOMessage umoMessage = routeMessage(new MuleMessage(adapter), endpoint.isSynchronous());
                        if (umoMessage != null) {
                            postProcessor.doPostProcess(umoMessage.getPayload());
                        }
                    }
                } catch (RoutingException e) {
                    logger.error("Unable to route." + ExceptionUtils.getStackTrace(e));

                    // routingError is reset to false at the beginning of the
                    // poll method
                    routingError = true;

                    if (errorDir != null) {
                        logger.error("Moving file to error directory: " + errorDir);

                        VariableFilenameParser filenameParser = (VariableFilenameParser) fileConnector
                                .getFilenameParser();
                        filenameParser.setChannelId(fileConnector.getChannelId());

                        destinationDir = filenameParser.getFilename(adapter, errorDir);
                        destinationName = file.getName();
                    }
                } catch (Throwable t) {
                    logger.error("Error reading file " + file.getAbsolutePath() + "\n" + t.getMessage());
                    fileProcessedException = new MuleException(
                            new Message(Messages.FAILED_TO_READ_PAYLOAD, file.getName()));
                }

                // move the file if needed
                if (destinationDir != null) {
                    deleteFile(destinationName, destinationDir, true);

                    resultOfFileMoveOperation = renameFile(file.getName(), readDir, destinationName,
                            destinationDir);

                    if (!resultOfFileMoveOperation) {
                        throw new MuleException(new Message("file", 4, pathname(file.getName(), readDir),
                                pathname(destinationName, destinationDir)));
                    }
                } else if (fileConnector.isAutoDelete()) {
                    // adapter.getPayloadAsBytes();

                    resultOfFileMoveOperation = deleteFile(file.getName(), readDir, false);

                    if (!resultOfFileMoveOperation) {
                        throw new MuleException(new Message("file", 3, pathname(file.getName(), readDir)));
                    }
                }

                if (fileProcessedException != null) {
                    throw fileProcessedException;
                }
            }
        } catch (Exception e) {
            alertController.sendAlerts(((FileConnector) connector).getChannelId(), Constants.ERROR_403, "", e);
            handleException(e);
        }
    }

    /** Convert a directory path and a filename into a pathname */
    private String pathname(String name, String dir) {

        if (dir != null && dir.length() > 0) {

            return dir + "/" + name;
        } else {

            return name;
        }
    }

    /** Process a single file as a batched message source */
    private void processBatch(FileInfo file) throws Exception {
        UMOEndpointURI uri = endpoint.getEndpointURI();
        Protocol protocol = Protocol.valueOf(fileConnector.getInboundProtocol());
        Adaptor adaptor = AdaptorFactory.getAdaptor(protocol);

        if (adaptor instanceof BatchAdaptor) {
            BatchAdaptor batchAdaptor = (BatchAdaptor) adaptor;
            FileSystemConnection con = fileConnector.getConnection(uri, null);
            Reader in = null;
            try {
                in = new InputStreamReader(con.readFile(file.getName(), readDir),
                        fileConnector.getCharsetEncoding());
                Map<String, String> protocolProperties = fileConnector.getProtocolProperties();
                protocolProperties.put("batchScriptId", fileConnector.getChannelId());
                batchAdaptor.processBatch(in, fileConnector.getProtocolProperties(), this, endpoint);
            } finally {
                if (in != null) {
                    in.close();
                }
                con.closeReadFile();
                fileConnector.releaseConnection(uri, con, null);
            }
        } else {
            throw new Exception("Data type " + protocol + " does not support batch processing.");
        }
    }

    /** Delete a file */
    private boolean deleteFile(String name, String dir, boolean mayNotExist) throws Exception {
        UMOEndpointURI uri = endpoint.getEndpointURI();
        FileSystemConnection con = fileConnector.getConnection(uri, null);
        try {
            con.delete(name, dir, mayNotExist);
            return true;
        } catch (Exception e) {
            if (mayNotExist) {
                return true;
            } else {
                logger.info("Unable to delete destination file");
                return false;
            }
        } finally {
            fileConnector.releaseConnection(uri, con, null);
        }
    }

    private boolean renameFile(String fromName, String fromDir, String toName, String toDir) throws Exception {

        UMOEndpointURI uri = endpoint.getEndpointURI();
        FileSystemConnection con = fileConnector.getConnection(uri, null);
        try {

            con.move(fromName, fromDir, toName, toDir);
            return true;
        } catch (Exception e) {

            return false;
        } finally {
            fileConnector.releaseConnection(uri, con, null);
        }
    }

    // Returns the contents of the file in a byte array.
    private byte[] getBytesFromFile(FileInfo file) throws Exception {

        UMOEndpointURI uri = endpoint.getEndpointURI();
        FileSystemConnection con = fileConnector.getConnection(uri, null);

        try {
            InputStream is = con.readFile(file.getName(), readDir);

            // Get the size of the file
            long length = file.getSize();

            // You cannot create an array using a long type.
            // It needs to be an int type.
            // Before converting to an int type, check
            // to ensure that file is not larger than Integer.MAX_VALUE.
            if (length > Integer.MAX_VALUE) {
                // File is too large
                // TODO: throw new
                // ??Exception("Implementation restriction: file too large.");
            }

            // Create the byte array to hold the data
            byte[] bytes = new byte[(int) length];

            // Read in the bytes
            int offset = 0;
            int numRead = 0;
            while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
                offset += numRead;
            }

            // Ensure all the bytes have been read in
            if (offset < bytes.length) {
                throw new IOException("Could not completely read file " + file.getName());
            }

            // Close the input stream and return bytes
            is.close();
            con.closeReadFile();
            return bytes;
        } finally {
            fileConnector.releaseConnection(uri, con, null);
        }
    }

    /**
     * Get a list of files to be processed.
     * 
     * @return a list of files to be processed.
     * @throws org.mule.MuleException
     *             which will wrap any other exceptions or errors.
     */
    FileInfo[] listFiles() throws Exception {

        UMOEndpointURI uri = endpoint.getEndpointURI();
        FileSystemConnection con = fileConnector.getConnection(uri, null);

        try {
            return con.listFiles(readDir, filenamePattern, fileConnector.isRegex(), fileConnector.isIgnoreDot())
                    .toArray(new FileInfo[0]);
        } finally {
            fileConnector.releaseConnection(uri, con, null);
        }
    }

    public boolean isRoutingError() {
        return routingError;
    }

    public void setRoutingError(boolean routingError) {
        this.routingError = routingError;
    }

}