com.fizzed.blaze.ssh.impl.JschSftpSession.java Source code

Java tutorial

Introduction

Here is the source code for com.fizzed.blaze.ssh.impl.JschSftpSession.java

Source

/*
 * Copyright 2015 Fizzed, Inc.
 *
 * Licensed 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 com.fizzed.blaze.ssh.impl;

import com.fizzed.blaze.ssh.SshException;
import com.fizzed.blaze.ssh.SshFile;
import com.fizzed.blaze.ssh.SshFileAttributes;
import com.fizzed.blaze.ssh.SshSession;
import com.fizzed.blaze.ssh.SshSftpException;
import com.fizzed.blaze.ssh.SshSftpGet;
import com.fizzed.blaze.ssh.SshSftpNoSuchFileException;
import com.fizzed.blaze.ssh.SshSftpPut;
import com.fizzed.blaze.ssh.SshSftpSession;
import com.fizzed.blaze.util.Streamable;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.SftpProgressMonitor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author joelauer
 */
public class JschSftpSession extends SshSftpSession implements SshSftpSupport {
    static private final Logger log = LoggerFactory.getLogger(JschSftpSession.class);

    private final SshSession session;
    private final ChannelSftp channel;
    private boolean closed;
    private Path workingDir;

    public JschSftpSession(SshSession session, ChannelSftp channel) {
        Objects.requireNonNull(session, "session cannot be null");
        Objects.requireNonNull(channel, "channel cannot be null");
        this.session = session;
        this.channel = channel;
        // somewhat unorthodox but we want the current working directory
        this.pwd();
    }

    @Override
    public SshSession session() {
        return this.session;
    }

    @Override
    public boolean closed() {
        return this.closed;
    }

    @Override
    public void close() throws IOException {
        if (!this.closed) {
            try {
                this.channel.disconnect();
            } catch (Exception e) {
                // not sure this matters
            }
            this.closed = true;
        }
    }

    // sub-actions?

    @Override
    public final Path pwd() {
        try {
            this.workingDir = Paths.get(this.channel.pwd());
            return this.workingDir;
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public void cd(String path) {
        cd(Paths.get(path));
    }

    @Override
    public void cd(Path path) throws SshException {
        try {
            this.channel.cd(PathHelper.toString(path));
            this.workingDir = path;
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public SshFileAttributes lstat(String path) throws SshSftpException {
        return lstat(Paths.get(path));
    }

    @Override
    public SshFileAttributes lstat(Path path) throws SshSftpException {
        try {
            String p = PathHelper.toString(path);
            log.debug("lstat {}", p);

            SftpATTRS attrs = this.channel.lstat(p);

            return new JschFileAttributes(attrs);
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public List<SshFile> ls(String path) {
        return ls(Paths.get(path));
    }

    @Override
    public List<SshFile> ls(Path path) throws SshException {
        try {
            @SuppressWarnings("UseOfObsoleteCollectionType")
            java.util.Vector<Object> fileObjects = this.channel.ls(PathHelper.toString(path));

            List<SshFile> files = new ArrayList<>();

            for (Object fileObject : fileObjects) {
                LsEntry entry = (LsEntry) fileObject;

                // seems like filtering these out is useful
                if (entry.getFilename().equals(".") || entry.getFilename().equals("..")) {
                    continue;
                }

                // workingDir + path + fileName
                Path entryPath = path.resolve(entry.getFilename()).normalize();

                files.add(new SshFile(entryPath, new JschFileAttributes(entry.getAttrs())));
            }

            return files;
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public SshSftpGet get() throws SshException {
        return new SshSftpGet(this);
    }

    @Override
    public void get(Path source, Streamable<OutputStream> target) throws SshException {
        try {
            // TODO: how can we log w/o requring it be a line???
            if (log.isInfoEnabled()) {
                System.out.print("[INFO] Downloading " + source + " -> " + target.path());
            }

            try {
                this.channel.get(source.toString(), target.stream(), new DefaultProgressMonitor(),
                        ChannelSftp.OVERWRITE, 0);
            } finally {
                IOUtils.closeQuietly(target);
            }
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public SshSftpPut put() throws SshException {
        return new SshSftpPut(this);
    }

    @Override
    public void put(Streamable<InputStream> source, String target) throws SshException {
        try {
            // target potentially 

            // TODO: how can we log w/o requring it be a complete line?
            if (log.isInfoEnabled()) {
                if (source.size() != null) {
                    System.out.print("[INFO] Uploading " + source.path() + " -> " + target + " (" + source.size()
                            + " bytes)");
                } else {
                    System.out.print("[INFO] Uploading " + source.path() + " -> " + target);
                }
            }

            OutputStream output = this.channel.put(target, new DefaultProgressMonitor(), ChannelSftp.OVERWRITE, 0);

            try {
                // copy streams input -> output
                IOUtils.copy(source.stream(), output);
            } finally {
                IOUtils.closeQuietly(output);
                IOUtils.closeQuietly(source);
            }
        } catch (IOException e) {
            throw new SshException(e.getMessage(), e);
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public void chgrp(String path, int gid) {
        chgrp(Paths.get(path), gid);
    }

    @Override
    public void chgrp(Path path, int gid) {
        try {
            this.channel.chgrp(gid, PathHelper.toString(path));
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public void chown(String path, int uid) {
        chown(Paths.get(path), uid);
    }

    @Override
    public void chown(Path path, int uid) {
        try {
            this.channel.chown(uid, PathHelper.toString(path));
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public void chown(String path, int uid, int gid) {
        chown(Paths.get(path), uid, gid);
    }

    @Override
    public void chown(Path path, int uid, int gid) {
        chown(path, uid);
        chgrp(path, gid);
    }

    @Override
    public void mkdir(String path) {
        mkdir(Paths.get(path));
    }

    @Override
    public void mkdir(Path path) {
        try {
            this.channel.mkdir(PathHelper.toString(path));
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public void rm(String path) {
        rm(Paths.get(path));
    }

    @Override
    public void rm(Path path) {
        try {
            this.channel.rm(PathHelper.toString(path));
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public void rmdir(String path) {
        rmdir(Paths.get(path));
    }

    @Override
    public void rmdir(Path path) {
        try {
            this.channel.rmdir(PathHelper.toString(path));
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public void mv(String source, String target) {
        mv(Paths.get(source), Paths.get(target));
    }

    @Override
    public void mv(Path source, Path target) {
        try {
            this.channel.rename(PathHelper.toString(source), PathHelper.toString(target));
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public void symlink(String target, String link) {
        symlink(Paths.get(target), Paths.get(link));
    }

    @Override
    public void symlink(Path target, Path link) {
        try {
            this.channel.symlink(PathHelper.toString(target), PathHelper.toString(link));
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public Path readlink(String target) {
        return readlink(Paths.get(target));
    }

    @Override
    public Path readlink(Path target) {
        try {
            return Paths.get(this.channel.readlink(PathHelper.toString(target)));
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    @Override
    public Path realpath(String target) {
        return realpath(Paths.get(target));
    }

    @Override
    public Path realpath(Path target) {
        try {
            return Paths.get(this.channel.realpath(PathHelper.toString(target)));
        } catch (SftpException e) {
            throw convertSftpException(e);
        }
    }

    private SshSftpException convertSftpException(SftpException e) {
        if (e.id == 2) {
            return new SshSftpNoSuchFileException(e.id, e.getMessage(), e);
        } else {
            return new SshSftpException(e.id, e.getMessage(), e);
        }
    }

    static public class DefaultProgressMonitor implements SftpProgressMonitor {
        @Override
        public void init(int op, String src, String dest, long max) {
            System.out.print(" ");
        }

        @Override
        public boolean count(long count) {
            System.out.print(".");
            return true;
        }

        @Override
        public void end() {
            System.out.println("!");
        }
    }

    static public class JschFileAttributes implements SshFileAttributes {

        private final SftpATTRS attrs;

        public JschFileAttributes(SftpATTRS attrs) {
            this.attrs = attrs;
        }

        @Override
        public FileTime lastModifiedTime() {
            return FileTime.fromMillis(attrs.getMTime() * 1000L);
        }

        @Override
        public FileTime lastAccessTime() {
            return FileTime.fromMillis(attrs.getATime() * 1000L);
        }

        @Override
        public FileTime creationTime() {
            // is this really not supported via sftp???
            //return FileTime.fromMillis(attrs.*1000L);
            return lastModifiedTime();
        }

        @Override
        public boolean isRegularFile() {
            return attrs.isReg();
        }

        @Override
        public boolean isDirectory() {
            return attrs.isDir();
        }

        @Override
        public boolean isSymbolicLink() {
            return attrs.isLink();
        }

        @Override
        public boolean isOther() {
            return attrs.isBlk() || attrs.isChr() || attrs.isFifo() || attrs.isSock();
        }

        @Override
        public long size() {
            return attrs.getSize();
        }

        @Override
        public Object fileKey() {
            // not available per specs
            return null;
        }

        @Override
        public int gid() {
            return attrs.getGId();
        }

        @Override
        public int uid() {
            return attrs.getUId();
        }

        @Override
        public Set<PosixFilePermission> permissions() {
            Set<PosixFilePermission> permissions = new HashSet<>();

            String s = attrs.getPermissionsString();

            // drwxrwxrwx

            if (s.charAt(1) == 'r') {
                permissions.add(PosixFilePermission.OWNER_READ);
            }

            if (s.charAt(2) == 'w') {
                permissions.add(PosixFilePermission.OWNER_WRITE);
            }

            if (s.charAt(3) == 'x' || s.charAt(3) == 's') {
                permissions.add(PosixFilePermission.OWNER_EXECUTE);
            }

            if (s.charAt(4) == 'r') {
                permissions.add(PosixFilePermission.GROUP_READ);
            }

            if (s.charAt(5) == 'w') {
                permissions.add(PosixFilePermission.GROUP_WRITE);
            }

            if (s.charAt(6) == 'x' || s.charAt(3) == 's') {
                permissions.add(PosixFilePermission.GROUP_EXECUTE);
            }

            if (s.charAt(7) == 'r') {
                permissions.add(PosixFilePermission.OTHERS_READ);
            }

            if (s.charAt(8) == 'w') {
                permissions.add(PosixFilePermission.OTHERS_WRITE);
            }

            if (s.charAt(9) == 'x' || s.charAt(3) == 's') {
                permissions.add(PosixFilePermission.OTHERS_EXECUTE);
            }

            return permissions;
        }
    }

}