2518 |
24 Jun 14 |
nicklas |
1 |
/** |
2518 |
24 Jun 14 |
nicklas |
$Id$ |
2518 |
24 Jun 14 |
nicklas |
3 |
|
2518 |
24 Jun 14 |
nicklas |
Copyright (C) 2014 Nicklas Nordborg |
2518 |
24 Jun 14 |
nicklas |
5 |
|
2518 |
24 Jun 14 |
nicklas |
This file is part of BASE - BioArray Software Environment. |
2518 |
24 Jun 14 |
nicklas |
Available at http://base.thep.lu.se/ |
2518 |
24 Jun 14 |
nicklas |
8 |
|
2518 |
24 Jun 14 |
nicklas |
BASE is free software; you can redistribute it and/or |
2518 |
24 Jun 14 |
nicklas |
modify it under the terms of the GNU General Public License |
2518 |
24 Jun 14 |
nicklas |
as published by the Free Software Foundation; either version 3 |
2518 |
24 Jun 14 |
nicklas |
of the License, or (at your option) any later version. |
2518 |
24 Jun 14 |
nicklas |
13 |
|
2518 |
24 Jun 14 |
nicklas |
BASE is distributed in the hope that it will be useful, |
2518 |
24 Jun 14 |
nicklas |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
2518 |
24 Jun 14 |
nicklas |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2518 |
24 Jun 14 |
nicklas |
GNU General Public License for more details. |
2518 |
24 Jun 14 |
nicklas |
18 |
|
2518 |
24 Jun 14 |
nicklas |
You should have received a copy of the GNU General Public License |
2518 |
24 Jun 14 |
nicklas |
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 |
Access remote files via SFTP protocol. URI:s should |
2518 |
24 Jun 14 |
nicklas |
use 'sftp://' as schema. The host and port of the URI should point to the |
2518 |
24 Jun 14 |
nicklas |
a server with SSH capabilities. A SSH fingerprint is required and must |
2518 |
24 Jun 14 |
nicklas |
match with the public key of the server. |
2518 |
24 Jun 14 |
nicklas |
47 |
|
2518 |
24 Jun 14 |
nicklas |
@author Nicklas |
2518 |
24 Jun 14 |
nicklas |
@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 |
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 |
// 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 |
// 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 |
// 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 |
// 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 |
// 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 |
Read metadata from the given file information. |
2518 |
24 Jun 14 |
nicklas |
<ul> |
2518 |
24 Jun 14 |
nicklas |
<li>Length: {@link FileAttributes#getSize()} |
2518 |
24 Jun 14 |
nicklas |
<li>Last modified: {@link FileAttributes#getMtime()} |
2518 |
24 Jun 14 |
nicklas |
</ul> |
7301 |
25 Aug 23 |
nicklas |
@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 |
} |