org.apache.nifi.processors.standard.util.SFTPUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.nifi.processors.standard.util.SFTPUtils.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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.apache.nifi.processors.standard.util;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;

import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.processor.Processor;
import org.apache.nifi.processor.util.StandardValidators;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;

public class SFTPUtils {

    public static final PropertyDescriptor SFTP_PRIVATEKEY_PATH = new PropertyDescriptor.Builder().required(false)
            .description("sftp.privatekey.path").defaultValue(null).name("sftp.privatekey.path")
            .addValidator(StandardValidators.FILE_EXISTS_VALIDATOR).sensitive(false).build();
    public static final PropertyDescriptor REMOTE_PASSWORD = new PropertyDescriptor.Builder().required(false)
            .description("remote.password").defaultValue(null).name("remote.password")
            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR).sensitive(true).build();
    public static final PropertyDescriptor SFTP_PRIVATEKEY_PASSPHRASE = new PropertyDescriptor.Builder()
            .required(false).description("sftp.privatekey.passphrase").defaultValue(null)
            .name("sftp.privatekey.passphrase").addValidator(StandardValidators.NON_EMPTY_VALIDATOR).sensitive(true)
            .build();
    public static final PropertyDescriptor SFTP_PORT = new PropertyDescriptor.Builder().required(false)
            .description("sftp.port").defaultValue(null).name("sftp.port")
            .addValidator(StandardValidators.NON_NEGATIVE_INTEGER_VALIDATOR).sensitive(false).build();
    public static final PropertyDescriptor NETWORK_DATA_TIMEOUT = new PropertyDescriptor.Builder().required(false)
            .description("network.data.timeout").defaultValue(null).name("network.data.timeout")
            .addValidator(StandardValidators.INTEGER_VALIDATOR).sensitive(false).build();
    public static final PropertyDescriptor SFTP_HOSTKEY_FILENAME = new PropertyDescriptor.Builder().required(false)
            .description("sftp.hostkey.filename").defaultValue(null).name("sftp.hostkey.filename")
            .addValidator(StandardValidators.FILE_EXISTS_VALIDATOR).sensitive(false).build();
    public static final PropertyDescriptor NETWORK_CONNECTION_TIMEOUT = new PropertyDescriptor.Builder()
            .required(false).description("network.connection.timeout").defaultValue(null)
            .name("network.connection.timeout").addValidator(StandardValidators.INTEGER_VALIDATOR).sensitive(false)
            .build();

    // required properties
    public static final PropertyDescriptor REMOTE_HOSTNAME = new PropertyDescriptor.Builder().required(true)
            .description("remote.hostname").defaultValue(null).name("remote.hostname")
            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR).sensitive(false).build();
    public static final PropertyDescriptor REMOTE_USERNAME = new PropertyDescriptor.Builder().required(true)
            .description("remote.username").defaultValue(null).name("remote.username")
            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR).sensitive(false).build();

    private static final List<PropertyDescriptor> propertyDescriptors = new ArrayList<>();

    static {
        JSch.setLogger(SFTPUtils.createLogger());
        propertyDescriptors.add(SFTP_PRIVATEKEY_PATH);
        propertyDescriptors.add(REMOTE_PASSWORD);
        propertyDescriptors.add(SFTP_PRIVATEKEY_PASSPHRASE);
        propertyDescriptors.add(SFTP_PORT);
        propertyDescriptors.add(NETWORK_DATA_TIMEOUT);
        propertyDescriptors.add(SFTP_HOSTKEY_FILENAME);
        propertyDescriptors.add(NETWORK_CONNECTION_TIMEOUT);
        propertyDescriptors.add(REMOTE_USERNAME);
        propertyDescriptors.add(REMOTE_HOSTNAME);
    }

    private static final Log logger = LogFactory.getLog(SFTPUtils.class);

    public static List<PropertyDescriptor> getPropertyDescriptors() {
        return propertyDescriptors;
    }

    public static SFTPConnection connectSftp(final SFTPConfiguration conf)
            throws JSchException, SftpException, IOException {
        final JSch jsch = new JSch();
        final Session session = SFTPUtils.createSession(conf, jsch);
        final ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp");
        sftp.connect();
        return new SFTPConnection(session, sftp);
    }

    public static void changeWorkingDirectory(final ChannelSftp sftp, final String dirPath,
            final boolean createDirs, final Processor proc) throws IOException {
        final Deque<String> stack = new LinkedList<>();
        File dir = new File(dirPath);
        String currentWorkingDirectory = null;
        boolean dirExists = false;
        final String forwardPaths = dir.getPath().replaceAll(Matcher.quoteReplacement("\\"),
                Matcher.quoteReplacement("/"));
        try {
            currentWorkingDirectory = sftp.pwd();
            logger.debug(proc + " attempting to change directory from " + currentWorkingDirectory + " to "
                    + dir.getPath());
            //always use forward paths for long string attempt
            sftp.cd(forwardPaths);
            dirExists = true;
            logger.debug(proc + " changed working directory to '" + forwardPaths + "' from '"
                    + currentWorkingDirectory + "'");
        } catch (final SftpException sftpe) {
            logger.debug(proc + " could not change directory to '" + forwardPaths + "' from '"
                    + currentWorkingDirectory + "' so trying the hard way.");
        }
        if (dirExists) {
            return;
        }
        if (!createDirs) {
            throw new IOException("Unable to change to requested working directory \'" + forwardPaths
                    + "\' but not configured to create dirs.");
        }

        do {
            stack.push(dir.getName());
        } while ((dir = dir.getParentFile()) != null);

        String dirName = null;
        while ((dirName = stack.peek()) != null) {
            stack.pop();
            //find out if exists, if not make it if configured to do so or throw exception
            dirName = ("".equals(dirName.trim())) ? "/" : dirName;
            try {
                sftp.cd(dirName);
            } catch (final SftpException sftpe) {
                logger.debug(proc + " creating new directory and changing to it " + dirName);
                try {
                    sftp.mkdir(dirName);
                    sftp.cd(dirName);
                } catch (final SftpException e) {
                    throw new IOException(proc + " could not make/change directory to [" + dirName + "] ["
                            + e.getLocalizedMessage() + "]", e);
                }
            }
        }
    }

    public static Session createSession(final SFTPConfiguration conf, final JSch jsch)
            throws JSchException, IOException {
        if (conf == null || null == jsch) {
            throw new NullPointerException();
        }

        final Hashtable<String, String> newOptions = new Hashtable<>();

        Session session = jsch.getSession(conf.username, conf.hostname, conf.port);

        final String hostKeyVal = conf.hostkeyFile;

        if (null != hostKeyVal) {
            try {
                jsch.setKnownHosts(hostKeyVal);
            } catch (final IndexOutOfBoundsException iob) {
                throw new IOException(
                        "Unable to establish connection due to bad known hosts key file " + hostKeyVal, iob);
            }
        } else {
            newOptions.put("StrictHostKeyChecking", "no");
            session.setConfig(newOptions);
        }

        final String privateKeyVal = conf.privatekeyFile;
        if (null != privateKeyVal) {
            jsch.addIdentity(privateKeyVal, conf.privateKeypassphrase);
        }

        if (null != conf.password) {
            session.setPassword(conf.password);
        }

        session.setTimeout(conf.connectionTimeout); //set timeout for connection
        session.connect();
        session.setTimeout(conf.dataTimeout); //set timeout for data transfer

        return session;
    }

    public static com.jcraft.jsch.Logger createLogger() {

        return new com.jcraft.jsch.Logger() {

            @Override
            public boolean isEnabled(int level) {
                return true;
            }

            @Override
            public void log(int level, String message) {
                logger.debug("SFTP Log: " + message);
            }
        };
    }

    public static class SFTPConfiguration {

        private String hostname;
        private String username;
        private int port = 22;
        private int connectionTimeout = 0;
        private int dataTimeout = 0;
        private String hostkeyFile;
        private String privatekeyFile;
        private String password;
        private String privateKeypassphrase;

        public SFTPConfiguration() {
        }

        public void setHostname(final String val) {
            this.hostname = val;
        }

        public String getHostname() {
            return hostname;
        }

        public void setUsername(final String val) {
            this.username = val;
        }

        public String getUsername() {
            return username;
        }

        public void setPort(final String val) {
            if (val != null) {
                port = Integer.parseInt(val);
            }
        }

        public void setConnectionTimeout(final String val) {
            if (val != null) {
                connectionTimeout = Integer.parseInt(val);
            }
        }

        public void setDataTimeout(final String val) {
            if (val != null) {
                dataTimeout = Integer.parseInt(val);
            }
        }

        public void setHostkeyFile(final String val) {
            this.hostkeyFile = val;
        }

        public void setPrivateKeyFile(final String val) {
            this.privatekeyFile = val;
        }

        public void setPassword(final String val) {
            this.password = val;
        }

        public void setPrivateKeyPassphrase(final String val) {
            this.privateKeypassphrase = val;
        }
    }
}