All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fr.jayasoft.ivy.repository.ssh.SshCache Maven / Gradle / Ivy

The newest version!
package fr.jayasoft.ivy.repository.ssh;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

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

import fr.jayasoft.ivy.IvyContext;
import fr.jayasoft.ivy.event.IvyEvent;
import fr.jayasoft.ivy.event.IvyListener;
import fr.jayasoft.ivy.event.resolve.EndResolveEvent;
import fr.jayasoft.ivy.util.Credentials;
import fr.jayasoft.ivy.util.CredentialsUtil;
import fr.jayasoft.ivy.util.Message;

/**
 * a class to cache SSH Connections and Channel for the SSH Repository
 * each session is defined by connecting user / host / port
 * two maps are used to find cache entries
 * one map is using the above keys, the other
 * uses the session itself
 */
public class SshCache {
    
    private SshCache() {};
    
    private static SshCache instance = new SshCache();
    
    public static SshCache getInstance() {
        return instance;
    }
    
    private class Entry {
        private Session session = null;
        private ChannelSftp channelSftp = null;
        private String host = null;
        private String user = null;
        private int port = 22;
        
        /**
         * @return the host
         */
        public String getHost() {
            return host;
        }

        /**
         * @return the port
         */
        public int getPort() {
            return port;
        }

        /**
         * @return the user
         */
        public String getUser() {
            return user;
        }

        public Entry(Session newSession, String newUser, String newHost, int newPort) {
            session = newSession;
            host = newHost;
            user = newUser;
            port = newPort;
            IvyContext.getContext().getIvy().addIvyListener(new IvyListener() {
                public void progress(IvyEvent event) {
                    event.getSource().removeIvyListener(this);
                    clearSession(session);
                }
            }, EndResolveEvent.NAME);
        }

        /**
         * attach an sftp channel to this cache entry
         * @param channelSftp to attach
         */
        public void setChannelSftp(ChannelSftp newChannel) {
            if(channelSftp != null && newChannel != null )
                throw new IllegalStateException("Only one sftp channelSftp per session allowed");
            this.channelSftp = newChannel;
        }
        
        /**
         * @return the attached sftp channel
         */
        public ChannelSftp getChannelSftp() {
            return channelSftp;
        }
        
        /**
         * @return the session
         */
        private Session getSession() {
            return session;
        }

        /**
         * remove channelSftp and disconnect if necessary
         */
        public void releaseChannelSftp() {
            if(channelSftp != null) {
                if(channelSftp.isConnected()) {
                    Message.verbose(":: SFTP :: closing sftp connection from "+host+"...");
                    channelSftp.disconnect();
                    channelSftp = null;
                    Message.verbose(":: SFTP :: sftp connection closed from "+host);
                }
            }
        }
    }
    /**
     * key is username / host / port
     * @see SshCache.createCacheKey() for details
     */
    private Map uriCacheMap = new HashMap();
    /**
     * key is the session itself
     */
    private Map sessionCacheMap = new HashMap();
    
    /**
     * retrieves a session entry for a given hostname from the cache
     * @param hostname to retrieve session for
     * @return null or the existing entry
     */
    private Entry getCacheEntry(String user, String host, int port) {
        return (Entry)uriCacheMap.get(createCacheKey(user, host, port));
    }

    /**
     * Creates a cobined cache key from the given key parts
     * @param user name of the user
     * @param host of the connection
     * @param port of the connection
     * @return key for the cache
     */
    private static String createCacheKey(String user, String host, int port) {
        String portToUse = "22";
        if(port != -1 && port != 22)
            portToUse = Integer.toString(port);
        return user.toLowerCase().trim()+"@"+host.toLowerCase().trim()+":"+portToUse;
    }
    
    /**
     * retrieves a session entry for a given session from the cache
     * @param session to retrieve cache entry for
     * @return null or the existing entry
     */
    private Entry getCacheEntry(Session session) {
        return (Entry)sessionCacheMap.get(session);
    }

    /**
     * Sets a session to a given combined key into the cache
     * If an old session object already exists, close and remove it
     * @param user of the session
     * @param host of the session
     * @param port of the session
     * @param session Session to save
     */
    private void setSession(String user, String host, int port, Session newSession) {
        Entry entry = (Entry)uriCacheMap.get(createCacheKey(user, host, port));
        Session oldSession = null;
        if(entry != null)
            oldSession = entry.getSession();
        if(oldSession != null && !oldSession.equals(newSession) && 
           oldSession.isConnected()) {
            entry.releaseChannelSftp();
            String oldhost = oldSession.getHost();
            Message.verbose(":: SSH :: closing ssh connection from "+oldhost+"...");
            oldSession.disconnect();
            Message.verbose(":: SSH :: ssh connection closed from "+oldhost);
        }
        if((newSession == null) && (entry != null)) {
            uriCacheMap.remove(createCacheKey(user, host, port));
            if(entry.getSession() != null)
                sessionCacheMap.remove(entry.getSession());
        } else {
            Entry newEntry = new Entry(newSession,user,host,port);
            uriCacheMap.put(createCacheKey(user, host, port), newEntry);
            sessionCacheMap.put(newSession, newEntry);
        }
    }

    /**
     * discardes session entries from the cache
     * @param session to clear
     */
    public void clearSession(Session session) {
        Entry entry = (Entry)sessionCacheMap.get(session);
        if(entry != null) 
            setSession(entry.getUser(), entry.getHost(), entry.getPort(), null);
    }
    
    /**
     * retrieves an sftp channel from the cache
     * @param host to connect to
     * @return channelSftp or null if not successful (channel not existent or dead)
     */
    public ChannelSftp getChannelSftp(Session session) throws IOException {
        ChannelSftp channel = null;
        Entry entry = getCacheEntry(session);
        if(entry != null) {
            channel = entry.getChannelSftp();
            if(channel != null && !channel.isConnected()) {
                entry.releaseChannelSftp();
                channel = null;
            }
        }    
        return channel;
    }
    
    /**
     * attaches a channelSftp to an existing session cache entry
     * @param session to attach the channel to
     * @param channelSftp channel to attach
     */
    public void attachChannelSftp(Session session, ChannelSftp channel) {
        Entry entry = getCacheEntry(session);
        if(entry == null)
            throw new IllegalArgumentException("No entry for "+session+" in the cache");
        entry.setChannelSftp(channel);
    }
    
    /**
     * Gets a session from the cache or establishes a new session if necessary
     * @param username for the session to use
     * @param host to connect to
     * @param port to use for session (-1 == use standard port)
     * @param password to use for authentication (optional)
     * @param pemFile File to use for public key authentication
     * @param pemPassword to use for accessing the pemFile (optional)
     * @param passFile to store credentials
     * @return session or null if not successful
     */
    public Session getSession(String host, 
                              int port,
                              String username,
                              String userPassword,
                              File pemFile,
                              String pemPassword,
                              File passFile) throws IOException {
        Entry entry = getCacheEntry(username, host, port);
        Session session = null;
        if(entry != null)
            session = entry.getSession();
        if(session == null || !session.isConnected()) {
            Message.verbose(":: SSH :: connecting to "+host+"...");
            try {
                JSch jsch=new JSch();
                if(port != -1)
                    session = jsch.getSession(username,host,port);
                else
                    session = jsch.getSession(username,host);
                if(pemFile != null) {
                    jsch.addIdentity(pemFile.getAbsolutePath(), pemPassword);
                } 
                session.setUserInfo(new cfUserInfo(host,username,userPassword,pemFile,pemPassword,passFile));
                session.connect();
                Message.verbose(":: SSH :: connected to "+host+"!");
                setSession(username, host, port, session);
            } catch (JSchException e) {
                if (passFile.exists()) {
                    passFile.delete();
                }
                IOException ex = new IOException(e.getMessage());
                ex.initCause(e);
                throw ex;
            }
        }
        return session;
    }
    
    /**
     * feeds in password silently into JSch
     */
    private static class cfUserInfo implements UserInfo {
        
        private String userPassword;
        private String pemPassword;
        private String userName;
        private final File pemFile;
        private final String host;
        private final File passfile;
        
        public cfUserInfo(String host, String userName, String userPassword, File pemFile, String pemPassword, File passfile) {
            this.userPassword = userPassword;
            this.pemPassword = pemPassword;
            this.host = host;
            this.passfile = passfile;
            this.userName = userName;
            this.pemFile = pemFile;
        }
        
        public void showMessage(String message) {
            Message.info(message);
        }

        public boolean promptYesNo(String message) {
            return true;
        }

        public boolean promptPassword(String message) {
            return true;
        }

        public boolean promptPassphrase(String message) {
            return true;
        }

        public String getPassword() {
            if(userPassword == null) {
                Credentials c = CredentialsUtil.promptCredentials(new Credentials(null, host, userName, userPassword), passfile);
                if (c != null) {
                    userName = c.getUserName();
                    userPassword = c.getPasswd();
                }
            }
            return userPassword;
        }

        public String getPassphrase() {
            if(pemPassword == null && pemFile != null) {
                Credentials c = CredentialsUtil.promptCredentials(new Credentials(null, pemFile.getAbsolutePath(), userName, pemPassword), passfile);
                if (c != null) {
                    userName = c.getUserName();
                    pemPassword = c.getPasswd();
                }
            }
            return pemPassword;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy