extensions/net.sf.basedb.xfiles/trunk/src/net/sf/basedb/xfiles/sftp/SftpConnectionManager.java

Code
Comments
Other
Rev Date Author Line
2518 24 Jun 14 nicklas 1 /**
2518 24 Jun 14 nicklas 2   $Id$
2518 24 Jun 14 nicklas 3
2518 24 Jun 14 nicklas 4   Copyright (C) 2014 Nicklas Nordborg
2518 24 Jun 14 nicklas 5
2518 24 Jun 14 nicklas 6   This file is part of BASE - BioArray Software Environment.
2518 24 Jun 14 nicklas 7   Available at http://base.thep.lu.se/
2518 24 Jun 14 nicklas 8
2518 24 Jun 14 nicklas 9   BASE is free software; you can redistribute it and/or
2518 24 Jun 14 nicklas 10   modify it under the terms of the GNU General Public License
2518 24 Jun 14 nicklas 11   as published by the Free Software Foundation; either version 3
2518 24 Jun 14 nicklas 12   of the License, or (at your option) any later version.
2518 24 Jun 14 nicklas 13
2518 24 Jun 14 nicklas 14   BASE is distributed in the hope that it will be useful,
2518 24 Jun 14 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
2518 24 Jun 14 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2518 24 Jun 14 nicklas 17   GNU General Public License for more details.
2518 24 Jun 14 nicklas 18
2518 24 Jun 14 nicklas 19   You should have received a copy of the GNU General Public License
2518 24 Jun 14 nicklas 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
2518 24 Jun 14 nicklas 21 */
2518 24 Jun 14 nicklas 22 package net.sf.basedb.xfiles.sftp;
2518 24 Jun 14 nicklas 23
2520 25 Jun 14 nicklas 24 import java.io.BufferedInputStream;
2518 24 Jun 14 nicklas 25 import java.io.IOException;
2518 24 Jun 14 nicklas 26 import java.io.InputStream;
2518 24 Jun 14 nicklas 27 import java.net.URI;
2518 24 Jun 14 nicklas 28 import java.util.Date;
2518 24 Jun 14 nicklas 29
3804 22 Mar 16 nicklas 30 import net.schmizz.sshj.Config;
2518 24 Jun 14 nicklas 31 import net.schmizz.sshj.sftp.FileAttributes;
2518 24 Jun 14 nicklas 32 import net.schmizz.sshj.sftp.RemoteFile;
2518 24 Jun 14 nicklas 33 import net.sf.basedb.util.uri.CloseResourceInputStream;
2518 24 Jun 14 nicklas 34 import net.sf.basedb.util.uri.ConnectionParameters;
2522 25 Jun 14 nicklas 35 import net.sf.basedb.util.uri.ResumableConnectionManager;
2518 24 Jun 14 nicklas 36 import net.sf.basedb.util.uri.UriMetadata;
2571 13 Aug 14 nicklas 37 import net.sf.basedb.xfiles.ConnectionCache;
2572 13 Aug 14 nicklas 38 import net.sf.basedb.xfiles.ConnectionInfo;
2518 24 Jun 14 nicklas 39 import net.sf.basedb.xfiles.MultiCloseable;
2518 24 Jun 14 nicklas 40 import net.sf.basedb.xfiles.XFiles;
2518 24 Jun 14 nicklas 41
2518 24 Jun 14 nicklas 42 /**
2518 24 Jun 14 nicklas 43   Access remote files via SFTP protocol. URI:s should
2518 24 Jun 14 nicklas 44   use 'sftp://' as schema. The host and port of the URI should point to the 
2518 24 Jun 14 nicklas 45   a server with SSH capabilities. A SSH fingerprint is required and must
2518 24 Jun 14 nicklas 46   match with the public key of the server.
2518 24 Jun 14 nicklas 47
2518 24 Jun 14 nicklas 48   @author Nicklas
2518 24 Jun 14 nicklas 49   @since 1.0
2518 24 Jun 14 nicklas 50 */
2518 24 Jun 14 nicklas 51 public class SftpConnectionManager
2522 25 Jun 14 nicklas 52   implements ResumableConnectionManager
2518 24 Jun 14 nicklas 53 {
2518 24 Jun 14 nicklas 54
2571 13 Aug 14 nicklas 55   static ConnectionCache<SSHClientWrapper> CONNECTION_CACHE = new ConnectionCache<SSHClientWrapper>(new SftpConnectionParametersComparator());
2571 13 Aug 14 nicklas 56   
3804 22 Mar 16 nicklas 57   private final Config sshConfig;
2518 24 Jun 14 nicklas 58   private final URI uri;
2518 24 Jun 14 nicklas 59   private final ConnectionParameters parameters;
2518 24 Jun 14 nicklas 60   private UriMetadata metadata;
2518 24 Jun 14 nicklas 61   
3804 22 Mar 16 nicklas 62   public SftpConnectionManager(URI uri, ConnectionParameters parameters, Config sshConfig)
2518 24 Jun 14 nicklas 63   {
2518 24 Jun 14 nicklas 64     this.uri = uri;
2518 24 Jun 14 nicklas 65     this.parameters = parameters;
3804 22 Mar 16 nicklas 66     this.sshConfig = sshConfig;
2518 24 Jun 14 nicklas 67   }
2518 24 Jun 14 nicklas 68   
2518 24 Jun 14 nicklas 69   /*
2522 25 Jun 14 nicklas 70     From the ResumableConnectionManager interface
2522 25 Jun 14 nicklas 71     ---------------------------------------------
2518 24 Jun 14 nicklas 72   */
2518 24 Jun 14 nicklas 73   @Override
2518 24 Jun 14 nicklas 74   public URI getURI()
2518 24 Jun 14 nicklas 75   {
2518 24 Jun 14 nicklas 76     return uri;
2518 24 Jun 14 nicklas 77   }
2518 24 Jun 14 nicklas 78
2518 24 Jun 14 nicklas 79   @Override
2518 24 Jun 14 nicklas 80   public InputStream getInputStream() 
2518 24 Jun 14 nicklas 81     throws IOException
2518 24 Jun 14 nicklas 82   {
2522 25 Jun 14 nicklas 83     return getInputStream(0);
2522 25 Jun 14 nicklas 84   }
2522 25 Jun 14 nicklas 85
2522 25 Jun 14 nicklas 86   @Override
2522 25 Jun 14 nicklas 87   public InputStream getInputStream(long offset) 
2522 25 Jun 14 nicklas 88     throws IOException
2522 25 Jun 14 nicklas 89   {
2518 24 Jun 14 nicklas 90     URI uri = getURI();
2518 24 Jun 14 nicklas 91     InputStream stream = null;
2571 13 Aug 14 nicklas 92     SSHClientWrapper wrapper = null;
2518 24 Jun 14 nicklas 93     RemoteFile remote = null;
2518 24 Jun 14 nicklas 94     try
2518 24 Jun 14 nicklas 95     {
2571 13 Aug 14 nicklas 96       wrapper = connect(uri, parameters);
2571 13 Aug 14 nicklas 97       remote = wrapper.openUri(uri);
7301 25 Aug 23 nicklas 98       metadata = loadMetadata(new UriMetadata(uri), remote);
7301 25 Aug 23 nicklas 99       InputStream tmp = remote.new RemoteFileInputStream(offset);
2520 25 Jun 14 nicklas 100       stream = new CloseResourceInputStream(
2522 25 Jun 14 nicklas 101         new BufferedInputStream(tmp, XFiles.DEFAULT_BUFFER_SIZE), 
2571 13 Aug 14 nicklas 102         new MultiCloseable(remote, wrapper));
2518 24 Jun 14 nicklas 103     }
2518 24 Jun 14 nicklas 104     finally
2518 24 Jun 14 nicklas 105     {
2571 13 Aug 14 nicklas 106       // If no stream has been created we close the remote immediately
2571 13 Aug 14 nicklas 107       if (stream == null && remote != null)
2518 24 Jun 14 nicklas 108       {
2571 13 Aug 14 nicklas 109         MultiCloseable.closeAll(remote, wrapper);
2518 24 Jun 14 nicklas 110       }
2518 24 Jun 14 nicklas 111     }
2518 24 Jun 14 nicklas 112     return stream;        
2518 24 Jun 14 nicklas 113   }
2518 24 Jun 14 nicklas 114
2518 24 Jun 14 nicklas 115   @Override
2518 24 Jun 14 nicklas 116   public UriMetadata getMetadata() 
2518 24 Jun 14 nicklas 117     throws IOException
2518 24 Jun 14 nicklas 118   {
2518 24 Jun 14 nicklas 119     if (metadata == null) 
2518 24 Jun 14 nicklas 120     {
2518 24 Jun 14 nicklas 121       // Create new metadata object
2518 24 Jun 14 nicklas 122       URI uri = getURI();
2518 24 Jun 14 nicklas 123       metadata = new UriMetadata(uri);
2518 24 Jun 14 nicklas 124       // Connect to the remote server and load file information
2571 13 Aug 14 nicklas 125       SSHClientWrapper wrapper = null;
2518 24 Jun 14 nicklas 126       RemoteFile remote = null;
2518 24 Jun 14 nicklas 127       try
2518 24 Jun 14 nicklas 128       {
2571 13 Aug 14 nicklas 129         wrapper = connect(uri, parameters);
2571 13 Aug 14 nicklas 130         remote = wrapper.openUri(uri);
2518 24 Jun 14 nicklas 131         // Load the metadata
2518 24 Jun 14 nicklas 132         loadMetadata(metadata, remote);
2518 24 Jun 14 nicklas 133       }
2518 24 Jun 14 nicklas 134       finally
2518 24 Jun 14 nicklas 135       {
2518 24 Jun 14 nicklas 136         // Clean up
2571 13 Aug 14 nicklas 137         MultiCloseable.closeAll(remote, wrapper);
2518 24 Jun 14 nicklas 138       }
2518 24 Jun 14 nicklas 139     }
2518 24 Jun 14 nicklas 140     return metadata;
2518 24 Jun 14 nicklas 141   }
2518 24 Jun 14 nicklas 142   // --------------------------------------
2518 24 Jun 14 nicklas 143   
2518 24 Jun 14 nicklas 144   
2571 13 Aug 14 nicklas 145   public SSHClientWrapper connect(URI uri, ConnectionParameters parameters)
2518 24 Jun 14 nicklas 146     throws IOException
2518 24 Jun 14 nicklas 147   {
2572 13 Aug 14 nicklas 148     if (parameters == null)
2572 13 Aug 14 nicklas 149     {
2572 13 Aug 14 nicklas 150       throw new IOException("No file server specified for " + uri);
2572 13 Aug 14 nicklas 151     }
2572 13 Aug 14 nicklas 152     else if (parameters.getSshFingerprint() == null)
2572 13 Aug 14 nicklas 153     {
2572 13 Aug 14 nicklas 154       throw new IOException("No SSH fingerprint in connection parameters for "+ uri);
2572 13 Aug 14 nicklas 155     }
2572 13 Aug 14 nicklas 156     else if (parameters.getUsername() == null)
2572 13 Aug 14 nicklas 157     {
2572 13 Aug 14 nicklas 158       throw new IOException("No username in connection parameters for "+ uri);
2572 13 Aug 14 nicklas 159     }
4755 16 Apr 18 nicklas 160     byte[] privateKey = parameters.getSshPrivateKey();
4755 16 Apr 18 nicklas 161     if (parameters.getPassword() == null && (privateKey == null || privateKey.length == 0))
2572 13 Aug 14 nicklas 162     {
4755 16 Apr 18 nicklas 163       throw new IOException("No password or private key in connection parameters for "+ uri);
2572 13 Aug 14 nicklas 164     }
2572 13 Aug 14 nicklas 165     
3804 22 Mar 16 nicklas 166     ConnectionInfo info = new ConnectionInfo(uri, parameters, sshConfig);
2572 13 Aug 14 nicklas 167     SSHClientWrapper wrapper = CONNECTION_CACHE.getClient(info);
2571 13 Aug 14 nicklas 168     boolean connected = wrapper != null && wrapper.ssh.isConnected();
2571 13 Aug 14 nicklas 169     
2518 24 Jun 14 nicklas 170     try
2518 24 Jun 14 nicklas 171     {
2571 13 Aug 14 nicklas 172       if (!connected)
2518 24 Jun 14 nicklas 173       {
2572 13 Aug 14 nicklas 174         wrapper = new SSHClientWrapper(info);
2571 13 Aug 14 nicklas 175         connected = true;
2519 25 Jun 14 nicklas 176       }
2518 24 Jun 14 nicklas 177     }
2518 24 Jun 14 nicklas 178     finally
2518 24 Jun 14 nicklas 179     {
2571 13 Aug 14 nicklas 180       if (!connected)
2571 13 Aug 14 nicklas 181       {
2571 13 Aug 14 nicklas 182          MultiCloseable.close(wrapper);
2571 13 Aug 14 nicklas 183          wrapper = null;
2571 13 Aug 14 nicklas 184       }
2518 24 Jun 14 nicklas 185     }
2571 13 Aug 14 nicklas 186     return wrapper;
2518 24 Jun 14 nicklas 187   }
2518 24 Jun 14 nicklas 188   
2518 24 Jun 14 nicklas 189
2518 24 Jun 14 nicklas 190   /**
2518 24 Jun 14 nicklas 191     Read metadata from the given file information.
2518 24 Jun 14 nicklas 192     <ul>
2518 24 Jun 14 nicklas 193     <li>Length: {@link FileAttributes#getSize()}
2518 24 Jun 14 nicklas 194     <li>Last modified: {@link FileAttributes#getMtime()}
2518 24 Jun 14 nicklas 195     </ul>
7301 25 Aug 23 nicklas 196     @return The metadata instance
2518 24 Jun 14 nicklas 197   */
7301 25 Aug 23 nicklas 198   public UriMetadata loadMetadata(UriMetadata metadata, RemoteFile remote)
2518 24 Jun 14 nicklas 199     throws IOException
2518 24 Jun 14 nicklas 200   {
2518 24 Jun 14 nicklas 201     FileAttributes attr = remote.fetchAttributes();
2518 24 Jun 14 nicklas 202     metadata.setLength(attr.getSize());
2518 24 Jun 14 nicklas 203     metadata.setLastModified(new Date(attr.getMtime() * 1000));
7301 25 Aug 23 nicklas 204     return metadata;
2518 24 Jun 14 nicklas 205   }
2518 24 Jun 14 nicklas 206   
2518 24 Jun 14 nicklas 207 }