org.mule.transport.sftp.SftpConnector.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.transport.sftp.SftpConnector.java

Source

/*
 * $Id$
 * --------------------------------------------------------------------------------------
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 *
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */

package org.mule.transport.sftp;

import org.mule.api.MuleContext;
import org.mule.api.MuleException;
import org.mule.api.construct.FlowConstruct;
import org.mule.api.endpoint.EndpointURI;
import org.mule.api.endpoint.ImmutableEndpoint;
import org.mule.api.endpoint.InboundEndpoint;
import org.mule.api.lifecycle.InitialisationException;
import org.mule.api.transport.ConnectorException;
import org.mule.api.transport.MessageReceiver;
import org.mule.config.i18n.CoreMessages;
import org.mule.transport.AbstractConnector;
import org.mule.transport.file.ExpressionFilenameParser;
import org.mule.transport.file.FilenameParser;
import org.mule.transport.sftp.notification.SftpNotifier;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool;

/**
 * <code>SftpConnector</code> sends and receives file messages over sftp using jsch
 * library Improves on SFTP with VFS Connector in the following ways: 1. Streams
 * files instead of reading them into memory. The SftpMessageReceiver is a
 * "non-materializing stream receiver" which does not read the file to memory. The
 * SftpMessageDispatcher also never materializes the stream and delegates the jsch
 * library for materialization. 3. Uses jsch library directly instead of using VFS as
 * middle-man. 3. More explicit connection lifefecyle management. 4. Leverages sftp
 * stat to determine if a file size changes (simpler and also less memory intensive)
 */
public class SftpConnector extends AbstractConnector {

    public static final String PROPERTY_POLLING_FREQUENCY = "pollingFrequency";
    public static final String PROPERTY_DIRECTORY = "directory";
    public static final String PROPERTY_OUTPUT_PATTERN = "outputPattern";
    public static final String PROPERTY_FILENAME = "filename";
    public static final String PROPERTY_ORIGINAL_FILENAME = "originalFilename";
    public static final String PROPERTY_SELECT_EXPRESSION = "selectExpression";
    public static final String PROPERTY_FILE_EXTENSION = "fileExtension";
    public static final String PROPERTY_INCLUDE_SUBFOLDERS = "includeSubfolders";
    public static final String PROPERTY_IDENTITY_FILE = "identityFile";
    public static final String PROPERTY_PASS_PHRASE = "passphrase";
    public static final String PROPERTY_FILE_AGE = "fileAge";
    public static final String PROPERTY_TEMP_DIR = "tempDir";
    public static final String PROPERTY_SIZE_CHECK_WAIT_TIME = "sizeCheckWaitTime";
    public static final String PROPERTY_ARCHIVE_DIR = "archiveDir";
    public static final String PROPERTY_ARCHIVE_TEMP_RECEIVING_DIR = "archiveTempReceivingDir";
    public static final String PROPERTY_ARCHIVE_TEMP_SENDING_DIR = "archiveTempSendingDir";
    public static final String PROPERTY_DUPLICATE_HANDLING = "duplicateHandling";
    public static final String PROPERTY_USE_TEMP_FILE_TIMESTAMP_SUFFIX = "useTempFileTimestampSuffix";
    public static final String PROPERTY_DUPLICATE_HANDLING_THROW_EXCEPTION = "throwException";
    public static final String PROPERTY_DUPLICATE_HANDLING_OVERWRITE = "overwrite";
    public static final String PROPERTY_DUPLICATE_HANDLING_ASS_SEQ_NO = "addSeqNo";
    public static final String PROPERTY_MAX_CONNECTION_POOL_SIZE = "maxConnectionPoolSize";
    public static final String PROPERTY_KEEP_FILE_ON_ERROR = "keepFileOnError";

    public static final int DEFAULT_POLLING_FREQUENCY = 1000;

    /**
     * logger used by this class
     */
    protected final static Log logger = LogFactory.getLog(SftpConnector.class);

    private FilenameParser filenameParser = new ExpressionFilenameParser();

    private long pollingFrequency;
    private boolean autoDelete = true;
    private String outputPattern;

    private String identityFile;
    private String passphrase;

    private boolean checkFileAge = false;
    private long fileAge = 0;

    private String tempDirInbound = null;
    private String tempDirOutbound = null;

    private Map<EndpointURI, GenericObjectPool> pools = new HashMap<EndpointURI, GenericObjectPool>();

    private String duplicateHandling = null;
    private Boolean useTempFileTimestampSuffix = null;
    private Long sizeCheckWaitTime = null;
    private String archiveDir = "";
    private String archiveTempReceivingDir = "";
    private String archiveTempSendingDir = "";

    /**
     * Should the file be kept if an error occurs when writing the file on the
     * outbound endpoint?
     */
    private Boolean keepFileOnError;

    /**
     * max pool size. 0 for no pool, -1 for no limit, otherwise the specified value
     */
    private int maxConnectionPoolSize;

    /**
     * Value that can be set via the System property
     * 'mule.sftp.transport.maxConnectionPoolSize'. If it's set the value is used
     * instead of <i>maxConnectionPoolSize</i>
     */
    private static final Integer overrideMaxConnectionPoolSize;

    static {
        String propValue = System.getProperty("mule.sftp.transport.maxConnectionPoolSize");
        if (propValue != null) {
            logger.info("Will override the maxConnectionPoolSize to " + propValue
                    + " from the system property 'mule.sftp.transport.maxConnectionPoolSize'.");
            overrideMaxConnectionPoolSize = Integer.parseInt(propValue);
        } else {
            overrideMaxConnectionPoolSize = null;
        }
    }

    public SftpConnector(MuleContext context) {
        super(context);
        filenameParser = new ExpressionFilenameParser();
    }

    public String getProtocol() {
        return "sftp";
    }

    @Override
    public MessageReceiver createReceiver(FlowConstruct flow, InboundEndpoint endpoint) throws Exception {
        long polling = pollingFrequency;

        // Override properties on the endpoint for the specific endpoint
        String tempPolling = (String) endpoint.getProperty(PROPERTY_POLLING_FREQUENCY);
        if (tempPolling != null) {
            polling = Long.parseLong(tempPolling);
        }

        if (polling <= 0) {
            polling = DEFAULT_POLLING_FREQUENCY;
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Set polling frequency to: " + polling);
        }

        return serviceDescriptor.createMessageReceiver(this, flow, endpoint, new Object[] { polling });
    }

    public SftpClient createSftpClient(ImmutableEndpoint endpoint) throws Exception {
        return createSftpClient(endpoint, null);
    }

    public SftpClient createSftpClient(ImmutableEndpoint endpoint, SftpNotifier notifier) throws Exception {
        SftpClient client = null;
        boolean ok = false;

        try {
            if (useConnectionPool()) {
                ObjectPool pool = getClientPool(endpoint);
                client = (SftpClient) pool.borrowObject();
            } else {
                client = SftpConnectionFactory.createClient(endpoint);
            }

            // We have to set the working directory before returning
            String dir = endpoint.getEndpointURI().getPath();
            client.changeWorkingDirectory(dir);
            if (logger.isDebugEnabled()) {
                logger.debug("Successfully changed working directory to: " + dir);
            }

            // TODO ML: Is this always necessary?
            client.setNotifier(notifier);

            ok = true;

        } finally {
            // Release the client if it was created but something failed after that,
            // otherwise we start to waste ssh-processes...
            if (!ok && client != null) {
                releaseClient(endpoint, client);
            }
        }

        return client;
    }

    /**
     * @return True if connection pooling is used, otherwise false
     */
    public boolean useConnectionPool() {
        return getMaxConnectionPoolSize() != 0;
    }

    public void releaseClient(ImmutableEndpoint endpoint, SftpClient client) throws Exception {
        if (useConnectionPool()) {
            if (getDispatcherFactory().isCreateDispatcherPerRequest()) {
                destroyClient(endpoint, client);
            } else {
                if (client != null && client.isConnected()) {
                    ObjectPool pool = getClientPool(endpoint);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Releasing connection for endpoint " + endpoint.getEndpointURI());
                    }
                    pool.returnObject(client);
                }
            }
        } else {
            client.disconnect();
        }
    }

    public void destroyClient(ImmutableEndpoint endpoint, SftpClient client) throws Exception {
        if (useConnectionPool()) {
            if ((client != null) && (client.isConnected())) {
                ObjectPool pool = getClientPool(endpoint);
                pool.invalidateObject(client);
            }
        }
    }

    protected synchronized ObjectPool getClientPool(ImmutableEndpoint endpoint) {
        GenericObjectPool pool = pools.get(endpoint.getEndpointURI());

        if (pool == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Pool is null - creating one for endpoint " + endpoint.getEndpointURI()
                        + " with max size " + getMaxConnectionPoolSize());
            }
            pool = new GenericObjectPool(new SftpConnectionFactory(endpoint), getMaxConnectionPoolSize());
            pool.setTestOnBorrow(isValidateConnections());
            pools.put(endpoint.getEndpointURI(), pool);
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("Using existing pool for endpoint " + endpoint.getEndpointURI() + ". Active: "
                        + pool.getNumActive() + ", Idle:" + pool.getNumIdle());
            }
        }

        return pool;
    }

    /*
     * (non-Javadoc)
     * @see org.mule.transport.AbstractConnector#doConnect()
     */
    protected void doConnect() throws Exception {
        // Do nothing!
    }

    /*
     * (non-Javadoc)
     * @see org.mule.transport.AbstractConnector#doDisconnect()
     */
    protected void doDisconnect() throws Exception {
        // Do nothing!
    }

    /*
     * (non-Javadoc)
     * @see org.mule.transport.AbstractConnector#doDispose()
     */
    protected void doDispose() {
        // Do nothing!
    }

    /*
     * (non-Javadoc)
     * @see org.mule.transport.AbstractConnector#doInitialise()
     */
    protected void doInitialise() throws InitialisationException {
        if (filenameParser != null) {
            filenameParser.setMuleContext(muleContext);
        }
    }

    /*
     * (non-Javadoc)
     * @see org.mule.transport.AbstractConnector#doStart()
     */
    protected void doStart() throws MuleException {
        // Do nothing!
    }

    /*
     * (non-Javadoc)
     * @see org.mule.transport.AbstractConnector#doStop()
     */
    protected void doStop() throws MuleException {
        if (logger.isDebugEnabled()) {
            logger.debug("Stopping all pools");
        }
        try {
            for (ObjectPool pool : pools.values()) {
                pool.close();
            }
        } catch (Exception e) {
            throw new ConnectorException(CoreMessages.failedToStop("SFTP Connector"), this, e);
        } finally {
            pools.clear();
        }
    }

    public long getPollingFrequency() {
        return pollingFrequency;
    }

    public void setPollingFrequency(long pollingFrequency) {
        this.pollingFrequency = pollingFrequency;
    }

    public FilenameParser getFilenameParser() {
        return filenameParser;
    }

    public void setFilenameParser(FilenameParser filenameParser) {
        this.filenameParser = filenameParser;
        if (filenameParser != null) {
            filenameParser.setMuleContext(muleContext);
        }
    }

    public String getOutputPattern() {
        return outputPattern;
    }

    public void setOutputPattern(String outputPattern) {
        this.outputPattern = outputPattern;
    }

    public boolean isAutoDelete() {
        return autoDelete;
    }

    public void setAutoDelete(boolean autoDelete) {
        this.autoDelete = autoDelete;
    }

    public String getIdentityFile() {
        return identityFile;
    }

    public void setIdentityFile(String identityFile) {
        this.identityFile = identityFile;
    }

    public String getPassphrase() {
        return passphrase;
    }

    public void setPassphrase(String passphrase) {
        this.passphrase = passphrase;
    }

    /**
     * Returns the file age.
     * 
     * @return Returns the fileAge in milliseconds.
     */
    public long getFileAge() {
        return fileAge;
    }

    /**
     * Sets the file age.
     * 
     * @param fileAge the fileAge in milliseconds to set.
     */
    public void setFileAge(long fileAge) {
        this.fileAge = fileAge;
        this.checkFileAge = true;
    }

    public boolean getCheckFileAge() {
        return checkFileAge;
    }

    public String getTempDirInbound() {
        return tempDirInbound;
    }

    public void setTempDirInbound(String pTempDirInbound) {
        tempDirInbound = pTempDirInbound;
    }

    public String getTempDirOutbound() {
        return tempDirOutbound;
    }

    public void setTempDirOutbound(String pTempDirOutbound) {
        tempDirOutbound = pTempDirOutbound;
    }

    // Need this method to be public for SftpNotifier
    @Override
    public boolean isEnableMessageEvents() {
        return super.isEnableMessageEvents();
    }

    public void setDuplicateHandling(String duplicateHandling) {
        this.duplicateHandling = duplicateHandling;
    }

    public String getDuplicateHandling() {
        return duplicateHandling;
    }

    public void setUseTempFileTimestampSuffix(Boolean useTempFileTimestampSuffix) {
        this.useTempFileTimestampSuffix = useTempFileTimestampSuffix;
    }

    public Boolean isUseTempFileTimestampSuffix() {
        return useTempFileTimestampSuffix;
    }

    public void setSizeCheckWaitTime(Long sizeCheckWaitTime) {
        this.sizeCheckWaitTime = sizeCheckWaitTime;
    }

    public Long getSizeCheckWaitTime() {
        return sizeCheckWaitTime;
    }

    public void setArchiveDir(String archiveDir) {
        this.archiveDir = archiveDir;
    }

    public String getArchiveDir() {
        return archiveDir;
    }

    public void setArchiveTempReceivingDir(String archiveTempReceivingDir) {
        this.archiveTempReceivingDir = archiveTempReceivingDir;
    }

    public String getArchiveTempReceivingDir() {
        return archiveTempReceivingDir;
    }

    public void setArchiveTempSendingDir(String archiveTempSendingDir) {
        this.archiveTempSendingDir = archiveTempSendingDir;
    }

    public String getArchiveTempSendingDir() {
        return archiveTempSendingDir;
    }

    /**
     * @see SftpConnector#maxConnectionPoolSize
     */
    public void setMaxConnectionPoolSize(int maxConnectionPoolSize) {
        this.maxConnectionPoolSize = maxConnectionPoolSize;
    }

    /**
     * @return the max connection pool size. If the system parameter
     *         mule.sftp.transport.maxConnectionPoolSize is set, that value will be
     *         used instead.
     */
    public int getMaxConnectionPoolSize() {
        if (overrideMaxConnectionPoolSize != null) {
            return overrideMaxConnectionPoolSize;
        }
        return maxConnectionPoolSize;
    }

    public Boolean isKeepFileOnError() {
        return keepFileOnError;
    }

    public void setKeepFileOnError(Boolean pKeepFileOnError) {
        keepFileOnError = pKeepFileOnError;
    }
}