1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs2.provider.ftp;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 import java.time.Instant;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.commons.net.ftp.FTPClient;
27 import org.apache.commons.net.ftp.FTPConnectionClosedException;
28 import org.apache.commons.net.ftp.FTPFile;
29 import org.apache.commons.net.ftp.FTPReply;
30 import org.apache.commons.vfs2.FileSystemException;
31 import org.apache.commons.vfs2.FileSystemOptions;
32 import org.apache.commons.vfs2.UserAuthenticationData;
33 import org.apache.commons.vfs2.provider.GenericFileName;
34 import org.apache.commons.vfs2.util.UserAuthenticatorUtils;
35
36
37
38
39
40
41
42 public class FTPClientWrapper implements FtpClient {
43
44 private static final Log LOG = LogFactory.getLog(FTPClientWrapper.class);
45
46
47
48
49 protected final FileSystemOptions fileSystemOptions;
50 private FTPClient ftpClient;
51 private final GenericFileName rootFileName;
52
53
54
55
56
57
58
59
60 protected FTPClientWrapper(final GenericFileName rootFileName, final FileSystemOptions fileSystemOptions)
61 throws FileSystemException {
62 this.rootFileName = rootFileName;
63 this.fileSystemOptions = fileSystemOptions;
64 getFtpClient();
65 }
66
67 @Override
68 public boolean abort() throws IOException {
69 try {
70
71
72
73
74 disconnect();
75 return true;
76 } catch (final IOException e) {
77 disconnect();
78 }
79 return true;
80 }
81
82 @Override
83 public OutputStream appendFileStream(final String relPath) throws IOException {
84 try {
85 return getFtpClient().appendFileStream(relPath);
86 } catch (final IOException e) {
87 disconnect();
88 return getFtpClient().appendFileStream(relPath);
89 }
90 }
91
92 @Override
93 public boolean completePendingCommand() throws IOException {
94 if (ftpClient != null) {
95 return getFtpClient().completePendingCommand();
96 }
97 return true;
98 }
99
100
101
102
103
104
105
106 private FTPClient createClient() throws FileSystemException {
107 final GenericFileName rootName = getRoot();
108 UserAuthenticationData authData = null;
109 try {
110 authData = UserAuthenticatorUtils.authenticate(fileSystemOptions, FtpFileProvider.AUTHENTICATOR_TYPES);
111 return createClient(rootName, authData);
112 } finally {
113 UserAuthenticatorUtils.cleanup(authData);
114 }
115 }
116
117
118
119
120
121
122
123
124
125 protected FTPClient createClient(final GenericFileName rootFileName, final UserAuthenticationData authData)
126 throws FileSystemException {
127 return FtpClientFactory.createConnection(rootFileName.getHostName(), rootFileName.getPort(),
128 UserAuthenticatorUtils.getData(authData, UserAuthenticationData.USERNAME,
129 UserAuthenticatorUtils.toChar(rootFileName.getUserName())),
130 UserAuthenticatorUtils.getData(authData, UserAuthenticationData.PASSWORD,
131 UserAuthenticatorUtils.toChar(rootFileName.getPassword())),
132 rootFileName.getPath(), getFileSystemOptions());
133 }
134
135 @Override
136 public boolean deleteFile(final String relPath) throws IOException {
137 try {
138 return getFtpClient().deleteFile(relPath);
139 } catch (final IOException e) {
140 disconnect();
141 return getFtpClient().deleteFile(relPath);
142 }
143 }
144
145 @Override
146 public void disconnect() throws IOException {
147 if (ftpClient != null) {
148 try {
149 ftpClient.quit();
150 } catch (final IOException e) {
151 LOG.debug("I/O exception while trying to quit, connection likely timed out, ignoring.", e);
152 } finally {
153 try {
154 getFtpClient().disconnect();
155 } catch (final IOException e) {
156 LOG.warn("I/O exception while trying to disconnect, connection likely closed, ignoring.", e);
157 } finally {
158 ftpClient = null;
159 }
160 }
161 }
162 }
163
164
165
166
167
168
169 public FileSystemOptions getFileSystemOptions() {
170 return fileSystemOptions;
171 }
172
173
174
175
176
177
178
179 FTPClient getFtpClient() throws FileSystemException {
180 if (ftpClient == null) {
181 ftpClient = createClient();
182 }
183 return ftpClient;
184 }
185
186 @Override
187 public int getReplyCode() throws IOException {
188 return getFtpClient().getReplyCode();
189 }
190
191 @Override
192 public String getReplyString() throws IOException {
193 return getFtpClient().getReplyString();
194 }
195
196
197
198
199
200
201 public GenericFileName getRoot() {
202 return rootFileName;
203 }
204
205
206
207
208 @Override
209 public boolean hasFeature(final String feature) throws IOException {
210 try {
211 return getFtpClient().hasFeature(feature);
212 } catch (final IOException ex) {
213 disconnect();
214 return getFtpClient().hasFeature(feature);
215 }
216 }
217
218 @Override
219 public boolean isConnected() throws FileSystemException {
220 return ftpClient != null && ftpClient.isConnected();
221 }
222
223 @Override
224 public FTPFile[] listFiles(final String relPath) throws IOException {
225 try {
226
227 return listFilesInDirectory(relPath);
228 } catch (final IOException e) {
229 disconnect();
230 return listFilesInDirectory(relPath);
231 }
232 }
233
234 private FTPFile[] listFilesInDirectory(final String relPath) throws IOException {
235
236 FTPFile[] ftpFiles = getFtpClient().listFiles(relPath);
237 if (FTPReply.isPositiveCompletion(getFtpClient().getReplyCode())) {
238 return ftpFiles;
239 }
240
241
242
243
244 String workingDirectory = null;
245 if (relPath != null) {
246 workingDirectory = getFtpClient().printWorkingDirectory();
247 if (!getFtpClient().changeWorkingDirectory(relPath)) {
248 return null;
249 }
250 }
251
252 ftpFiles = getFtpClient().listFiles();
253
254 if (relPath != null && !getFtpClient().changeWorkingDirectory(workingDirectory)) {
255 throw new FileSystemException("vfs.provider.ftp.wrapper/change-work-directory-back.error",
256 workingDirectory);
257 }
258 return ftpFiles;
259 }
260
261 @Override
262 public boolean makeDirectory(final String relPath) throws IOException {
263 try {
264 return getFtpClient().makeDirectory(relPath);
265 } catch (final IOException e) {
266 disconnect();
267 return getFtpClient().makeDirectory(relPath);
268 }
269 }
270
271
272
273
274 @Override
275 public Instant mdtmInstant(final String relPath) throws IOException {
276 try {
277 return getFtpClient().mdtmCalendar(relPath).toInstant();
278 } catch (final IOException ex) {
279 disconnect();
280 return getFtpClient().mdtmCalendar(relPath).toInstant();
281 }
282 }
283
284 @Override
285 public boolean removeDirectory(final String relPath) throws IOException {
286 try {
287 return getFtpClient().removeDirectory(relPath);
288 } catch (final IOException e) {
289 disconnect();
290 return getFtpClient().removeDirectory(relPath);
291 }
292 }
293
294 @Override
295 public boolean rename(final String oldName, final String newName) throws IOException {
296 try {
297 return getFtpClient().rename(oldName, newName);
298 } catch (final IOException e) {
299 disconnect();
300 return getFtpClient().rename(oldName, newName);
301 }
302 }
303
304 @Override
305 public InputStream retrieveFileStream(final String relPath) throws IOException {
306 try {
307 return getFtpClient().retrieveFileStream(relPath);
308 } catch (final IOException e) {
309 disconnect();
310 return getFtpClient().retrieveFileStream(relPath);
311 }
312 }
313
314 @Override
315 public InputStream retrieveFileStream(final String relPath, final int bufferSize) throws IOException {
316 try {
317 final FTPClient client = getFtpClient();
318 client.setBufferSize(bufferSize);
319 return client.retrieveFileStream(relPath);
320 } catch (final IOException e) {
321 disconnect();
322 final FTPClient client = getFtpClient();
323 client.setBufferSize(bufferSize);
324 return client.retrieveFileStream(relPath);
325 }
326 }
327
328 @Override
329 public InputStream retrieveFileStream(final String relPath, final long restartOffset) throws IOException {
330 try {
331 final FTPClient client = getFtpClient();
332 client.setRestartOffset(restartOffset);
333 return client.retrieveFileStream(relPath);
334 } catch (final IOException e) {
335 disconnect();
336 final FTPClient client = getFtpClient();
337 client.setRestartOffset(restartOffset);
338 return client.retrieveFileStream(relPath);
339 }
340 }
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363 public int sendOptions(final String commandName, String commandOptions) throws IOException {
364
365
366 return getFtpClient().sendCommand("OPTS", commandName + ' ' + commandOptions);
367 }
368
369 @Override
370 public void setBufferSize(final int bufferSize) throws FileSystemException {
371 getFtpClient().setBufferSize(bufferSize);
372 }
373
374 @Override
375 public OutputStream storeFileStream(final String relPath) throws IOException {
376 try {
377 return getFtpClient().storeFileStream(relPath);
378 } catch (final IOException e) {
379 disconnect();
380 return getFtpClient().storeFileStream(relPath);
381 }
382 }
383 }