embulk-input-ftp 0.1.6 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/classpath/embulk-input-ftp-0.2.0.jar +0 -0
- data/classpath/embulk-util-ftp-0.2.0.jar +0 -0
- data/src/main/java/org/embulk/input/FtpFileInputPlugin.java +124 -104
- data/src/test/java/org/embulk/input/TestFtpFileInputPlugin.java +308 -0
- data/src/test/resources/sample_01.csv +5 -0
- data/src/test/resources/sample_02.csv +5 -0
- metadata +6 -4
- data/classpath/embulk-input-ftp-0.1.6.jar +0 -0
- data/classpath/embulk-util-ftp-0.1.6.jar +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 27845469df3d11bb23cf8ef4a815733383a34768
|
4
|
+
data.tar.gz: 391a4e30104f40bb79d34b1780863bbac9fc32f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43298f5fba9022feb85f0523f99e703f69553fb47c1f1e41a0a34c78584bb162ff84291de2117367e26dae290d865bc90122e806baa8a10b1a3833ea2a5f3d2a
|
7
|
+
data.tar.gz: 663e55ceb928b929b00e8c53566617932b45dcdda8d4cb81d1c2b5a67ba2ed99387fbdc1d6fcd03b6e82e89c6615bacd27eb5d561b536f4be789659b1bc3cfe6
|
Binary file
|
Binary file
|
@@ -1,49 +1,52 @@
|
|
1
1
|
package org.embulk.input;
|
2
2
|
|
3
|
-
import java.util.List;
|
4
|
-
import java.util.ArrayList;
|
5
|
-
import java.util.Collections;
|
6
|
-
import java.util.concurrent.Executors;
|
7
|
-
import java.util.concurrent.ExecutorService;
|
8
|
-
import java.io.IOException;
|
9
|
-
import java.io.InterruptedIOException;
|
10
|
-
import java.io.InputStream;
|
11
|
-
import java.nio.channels.Channels;
|
12
|
-
import org.slf4j.Logger;
|
13
|
-
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
14
|
-
import com.google.common.collect.ImmutableList;
|
15
|
-
import com.google.common.base.Optional;
|
16
|
-
import com.google.common.base.Throwables;
|
17
3
|
import com.google.common.base.Function;
|
4
|
+
import com.google.common.base.Throwables;
|
5
|
+
import com.google.common.collect.ImmutableList;
|
6
|
+
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
7
|
+
import it.sauronsoftware.ftp4j.FTPAbortedException;
|
18
8
|
import it.sauronsoftware.ftp4j.FTPClient;
|
19
|
-
import it.sauronsoftware.ftp4j.FTPFile;
|
20
|
-
import it.sauronsoftware.ftp4j.FTPConnector;
|
21
9
|
import it.sauronsoftware.ftp4j.FTPCommunicationListener;
|
10
|
+
import it.sauronsoftware.ftp4j.FTPConnector;
|
11
|
+
import it.sauronsoftware.ftp4j.FTPDataTransferException;
|
22
12
|
import it.sauronsoftware.ftp4j.FTPDataTransferListener;
|
23
13
|
import it.sauronsoftware.ftp4j.FTPException;
|
14
|
+
import it.sauronsoftware.ftp4j.FTPFile;
|
24
15
|
import it.sauronsoftware.ftp4j.FTPIllegalReplyException;
|
25
|
-
import it.sauronsoftware.ftp4j.FTPDataTransferException;
|
26
|
-
import it.sauronsoftware.ftp4j.FTPAbortedException;
|
27
16
|
import it.sauronsoftware.ftp4j.FTPListParseException;
|
28
|
-
import org.embulk.config.TaskReport;
|
29
17
|
import org.embulk.config.Config;
|
30
|
-
import org.embulk.config.ConfigInject;
|
31
18
|
import org.embulk.config.ConfigDefault;
|
32
19
|
import org.embulk.config.ConfigDiff;
|
20
|
+
import org.embulk.config.ConfigInject;
|
33
21
|
import org.embulk.config.ConfigSource;
|
34
22
|
import org.embulk.config.Task;
|
23
|
+
import org.embulk.config.TaskReport;
|
35
24
|
import org.embulk.config.TaskSource;
|
36
25
|
import org.embulk.spi.BufferAllocator;
|
37
26
|
import org.embulk.spi.Exec;
|
38
27
|
import org.embulk.spi.FileInputPlugin;
|
39
28
|
import org.embulk.spi.TransactionalFileInput;
|
40
29
|
import org.embulk.spi.util.InputStreamFileInput;
|
30
|
+
import org.embulk.spi.util.InputStreamFileInput.InputStreamWithHints;
|
41
31
|
import org.embulk.spi.util.ResumableInputStream;
|
42
|
-
import org.embulk.spi.util.RetryExecutor.Retryable;
|
43
32
|
import org.embulk.spi.util.RetryExecutor.RetryGiveupException;
|
33
|
+
import org.embulk.spi.util.RetryExecutor.Retryable;
|
44
34
|
import org.embulk.util.ftp.BlockingTransfer;
|
45
35
|
import org.embulk.util.ftp.SSLPlugins;
|
46
36
|
import org.embulk.util.ftp.SSLPlugins.SSLPluginConfig;
|
37
|
+
import org.slf4j.Logger;
|
38
|
+
|
39
|
+
import java.io.IOException;
|
40
|
+
import java.io.InputStream;
|
41
|
+
import java.io.InterruptedIOException;
|
42
|
+
import java.nio.channels.Channels;
|
43
|
+
import java.util.ArrayList;
|
44
|
+
import java.util.Collections;
|
45
|
+
import java.util.List;
|
46
|
+
import java.util.Optional;
|
47
|
+
import java.util.concurrent.ExecutorService;
|
48
|
+
import java.util.concurrent.Executors;
|
49
|
+
|
47
50
|
import static org.embulk.spi.util.RetryExecutor.retryExecutor;
|
48
51
|
|
49
52
|
public class FtpFileInputPlugin
|
@@ -58,55 +61,55 @@ public class FtpFileInputPlugin
|
|
58
61
|
extends Task, SSLPlugins.SSLPluginTask
|
59
62
|
{
|
60
63
|
@Config("path_prefix")
|
61
|
-
|
64
|
+
String getPathPrefix();
|
62
65
|
|
63
66
|
@Config("last_path")
|
64
67
|
@ConfigDefault("null")
|
65
|
-
|
68
|
+
Optional<String> getLastPath();
|
66
69
|
|
67
70
|
@Config("incremental")
|
68
71
|
@ConfigDefault("true")
|
69
|
-
|
72
|
+
boolean getIncremental();
|
70
73
|
|
71
74
|
@Config("host")
|
72
|
-
|
75
|
+
String getHost();
|
73
76
|
|
74
77
|
@Config("port")
|
75
78
|
@ConfigDefault("null")
|
76
|
-
|
79
|
+
Optional<Integer> getPort();
|
77
80
|
|
78
81
|
@Config("user")
|
79
82
|
@ConfigDefault("null")
|
80
|
-
|
83
|
+
Optional<String> getUser();
|
81
84
|
|
82
85
|
@Config("password")
|
83
86
|
@ConfigDefault("null")
|
84
|
-
|
87
|
+
Optional<String> getPassword();
|
85
88
|
|
86
89
|
@Config("passive_mode")
|
87
90
|
@ConfigDefault("true")
|
88
|
-
|
91
|
+
boolean getPassiveMode();
|
89
92
|
|
90
93
|
@Config("ascii_mode")
|
91
94
|
@ConfigDefault("false")
|
92
|
-
|
95
|
+
boolean getAsciiMode();
|
93
96
|
|
94
97
|
@Config("ssl")
|
95
98
|
@ConfigDefault("false")
|
96
|
-
|
99
|
+
boolean getSsl();
|
97
100
|
|
98
101
|
@Config("ssl_explicit")
|
99
102
|
@ConfigDefault("true")
|
100
|
-
|
103
|
+
boolean getSslExplicit();
|
101
104
|
|
102
|
-
|
103
|
-
|
105
|
+
List<String> getFiles();
|
106
|
+
void setFiles(List<String> files);
|
104
107
|
|
105
|
-
|
106
|
-
|
108
|
+
SSLPluginConfig getSSLConfig();
|
109
|
+
void setSSLConfig(SSLPluginConfig config);
|
107
110
|
|
108
111
|
@ConfigInject
|
109
|
-
|
112
|
+
BufferAllocator getBufferAllocator();
|
110
113
|
}
|
111
114
|
|
112
115
|
@Override
|
@@ -146,7 +149,8 @@ public class FtpFileInputPlugin
|
|
146
149
|
if (task.getLastPath().isPresent()) {
|
147
150
|
configDiff.set("last_path", task.getLastPath().get());
|
148
151
|
}
|
149
|
-
}
|
152
|
+
}
|
153
|
+
else {
|
150
154
|
List<String> files = new ArrayList<String>(task.getFiles());
|
151
155
|
Collections.sort(files);
|
152
156
|
configDiff.set("last_path", files.get(files.size() - 1));
|
@@ -182,7 +186,7 @@ public class FtpFileInputPlugin
|
|
182
186
|
log.info("Using FTPS(FTPS/implicit) mode");
|
183
187
|
}
|
184
188
|
}
|
185
|
-
int port = task.getPort().isPresent()? task.getPort().get() : defaultPort;
|
189
|
+
int port = task.getPort().isPresent() ? task.getPort().get() : defaultPort;
|
186
190
|
|
187
191
|
client.addCommunicationListener(new LoggingCommunicationListner(log));
|
188
192
|
|
@@ -202,11 +206,11 @@ public class FtpFileInputPlugin
|
|
202
206
|
//client.setAutodetectUTF8
|
203
207
|
|
204
208
|
client.connect(task.getHost(), port);
|
205
|
-
log.info("Connecting to {}:{}",task.getHost(),port);
|
209
|
+
log.info("Connecting to {}:{}", task.getHost(), port);
|
206
210
|
|
207
211
|
if (task.getUser().isPresent()) {
|
208
|
-
log.info("Logging in with user "+task.getUser().get());
|
209
|
-
client.login(task.getUser().get(), task.getPassword().
|
212
|
+
log.info("Logging in with user " + task.getUser().get());
|
213
|
+
client.login(task.getUser().get(), task.getPassword().orElse(""));
|
210
214
|
}
|
211
215
|
|
212
216
|
log.info("Using passive mode");
|
@@ -215,7 +219,8 @@ public class FtpFileInputPlugin
|
|
215
219
|
if (task.getAsciiMode()) {
|
216
220
|
log.info("Using ASCII mode");
|
217
221
|
client.setType(FTPClient.TYPE_TEXTUAL);
|
218
|
-
}
|
222
|
+
}
|
223
|
+
else {
|
219
224
|
log.info("Using binary mode");
|
220
225
|
client.setType(FTPClient.TYPE_BINARY);
|
221
226
|
}
|
@@ -228,20 +233,20 @@ public class FtpFileInputPlugin
|
|
228
233
|
FTPClient connected = client;
|
229
234
|
client = null;
|
230
235
|
return connected;
|
231
|
-
|
232
|
-
|
233
|
-
log.info("FTP command failed: "+ex.getCode()+" "+ex.getMessage());
|
236
|
+
}
|
237
|
+
catch (FTPException ex) {
|
238
|
+
log.info("FTP command failed: " + ex.getCode() + " " + ex.getMessage());
|
234
239
|
throw Throwables.propagate(ex);
|
235
|
-
|
236
|
-
|
240
|
+
}
|
241
|
+
catch (FTPIllegalReplyException ex) {
|
237
242
|
log.info("FTP protocol error");
|
238
243
|
throw Throwables.propagate(ex);
|
239
|
-
|
240
|
-
|
241
|
-
log.info("FTP network error: "+ex);
|
244
|
+
}
|
245
|
+
catch (IOException ex) {
|
246
|
+
log.info("FTP network error: " + ex);
|
242
247
|
throw Throwables.propagate(ex);
|
243
|
-
|
244
|
-
|
248
|
+
}
|
249
|
+
finally {
|
245
250
|
if (client != null) {
|
246
251
|
disconnectClient(client);
|
247
252
|
}
|
@@ -253,11 +258,14 @@ public class FtpFileInputPlugin
|
|
253
258
|
if (client.isConnected()) {
|
254
259
|
try {
|
255
260
|
client.disconnect(false);
|
256
|
-
}
|
261
|
+
}
|
262
|
+
catch (FTPException ex) {
|
257
263
|
// do nothing
|
258
|
-
}
|
264
|
+
}
|
265
|
+
catch (FTPIllegalReplyException ex) {
|
259
266
|
// do nothing
|
260
|
-
}
|
267
|
+
}
|
268
|
+
catch (IOException ex) {
|
261
269
|
// do nothing
|
262
270
|
}
|
263
271
|
}
|
@@ -268,7 +276,8 @@ public class FtpFileInputPlugin
|
|
268
276
|
FTPClient client = newFTPClient(log, task);
|
269
277
|
try {
|
270
278
|
return listFilesByPrefix(log, client, task.getPathPrefix(), task.getLastPath());
|
271
|
-
}
|
279
|
+
}
|
280
|
+
finally {
|
272
281
|
disconnectClient(client);
|
273
282
|
}
|
274
283
|
}
|
@@ -281,12 +290,14 @@ public class FtpFileInputPlugin
|
|
281
290
|
if (prefix.isEmpty()) {
|
282
291
|
directory = "";
|
283
292
|
fileNamePrefix = "";
|
284
|
-
}
|
293
|
+
}
|
294
|
+
else {
|
285
295
|
int pos = prefix.lastIndexOf("/");
|
286
296
|
if (pos < 0) {
|
287
297
|
directory = "";
|
288
298
|
fileNamePrefix = prefix;
|
289
|
-
}
|
299
|
+
}
|
300
|
+
else {
|
290
301
|
directory = prefix.substring(0, pos + 1); // include last "/"
|
291
302
|
fileNamePrefix = prefix.substring(pos + 1);
|
292
303
|
}
|
@@ -308,29 +319,29 @@ public class FtpFileInputPlugin
|
|
308
319
|
listFilesRecursive(client, currentDirectory, file, lastPath, builder);
|
309
320
|
}
|
310
321
|
}
|
311
|
-
|
312
|
-
|
322
|
+
}
|
323
|
+
catch (FTPListParseException ex) {
|
313
324
|
log.info("FTP listing files failed");
|
314
325
|
throw Throwables.propagate(ex);
|
315
|
-
|
316
|
-
|
326
|
+
}
|
327
|
+
catch (FTPAbortedException ex) {
|
317
328
|
log.info("FTP listing files failed");
|
318
329
|
throw Throwables.propagate(ex);
|
319
|
-
|
320
|
-
|
330
|
+
}
|
331
|
+
catch (FTPDataTransferException ex) {
|
321
332
|
log.info("FTP data transfer failed");
|
322
333
|
throw Throwables.propagate(ex);
|
323
|
-
|
324
|
-
|
325
|
-
log.info("FTP command failed: "+ex.getCode()+" "+ex.getMessage());
|
334
|
+
}
|
335
|
+
catch (FTPException ex) {
|
336
|
+
log.info("FTP command failed: " + ex.getCode() + " " + ex.getMessage());
|
326
337
|
throw Throwables.propagate(ex);
|
327
|
-
|
328
|
-
|
338
|
+
}
|
339
|
+
catch (FTPIllegalReplyException ex) {
|
329
340
|
log.info("FTP protocol error");
|
330
341
|
throw Throwables.propagate(ex);
|
331
|
-
|
332
|
-
|
333
|
-
log.info("FTP network error: "+ex);
|
342
|
+
}
|
343
|
+
catch (IOException ex) {
|
344
|
+
log.info("FTP network error: " + ex);
|
334
345
|
throw Throwables.propagate(ex);
|
335
346
|
}
|
336
347
|
|
@@ -386,7 +397,7 @@ public class FtpFileInputPlugin
|
|
386
397
|
|
387
398
|
public void received(String statement)
|
388
399
|
{
|
389
|
-
log.info("< "+statement);
|
400
|
+
log.info("< " + statement);
|
390
401
|
}
|
391
402
|
|
392
403
|
public void sent(String statement)
|
@@ -395,7 +406,7 @@ public class FtpFileInputPlugin
|
|
395
406
|
// don't show password
|
396
407
|
return;
|
397
408
|
}
|
398
|
-
log.info("> "+statement);
|
409
|
+
log.info("> " + statement);
|
399
410
|
}
|
400
411
|
}
|
401
412
|
|
@@ -424,14 +435,14 @@ public class FtpFileInputPlugin
|
|
424
435
|
{
|
425
436
|
totalTransfer += length;
|
426
437
|
if (totalTransfer > nextTransferNotice) {
|
427
|
-
log.info("Transferred "+totalTransfer+" bytes");
|
428
|
-
nextTransferNotice = ((totalTransfer / transferNoticeBytes)+1) * transferNoticeBytes;
|
438
|
+
log.info("Transferred " + totalTransfer + " bytes");
|
439
|
+
nextTransferNotice = ((totalTransfer / transferNoticeBytes) + 1) * transferNoticeBytes;
|
429
440
|
}
|
430
441
|
}
|
431
442
|
|
432
443
|
public void completed()
|
433
444
|
{
|
434
|
-
log.info("Transfer completed "+totalTransfer+" bytes");
|
445
|
+
log.info("Transfer completed " + totalTransfer + " bytes");
|
435
446
|
}
|
436
447
|
|
437
448
|
public void aborted()
|
@@ -445,7 +456,7 @@ public class FtpFileInputPlugin
|
|
445
456
|
}
|
446
457
|
}
|
447
458
|
|
448
|
-
private static final long TRANSFER_NOTICE_BYTES = 100*1024*1024;
|
459
|
+
private static final long TRANSFER_NOTICE_BYTES = 100 * 1024 * 1024;
|
449
460
|
|
450
461
|
private static InputStream startDownload(final Logger log, final FTPClient client,
|
451
462
|
final String path, final long offset, ExecutorService executor)
|
@@ -460,30 +471,31 @@ public class FtpFileInputPlugin
|
|
460
471
|
{
|
461
472
|
try {
|
462
473
|
client.download(path, Channels.newOutputStream(transfer.getWriterChannel()), offset, new LoggingTransferListener(log, TRANSFER_NOTICE_BYTES));
|
463
|
-
|
464
|
-
|
465
|
-
log.info("FTP command failed: "+ex.getCode()+" "+ex.getMessage());
|
474
|
+
}
|
475
|
+
catch (FTPException ex) {
|
476
|
+
log.info("FTP command failed: " + ex.getCode() + " " + ex.getMessage());
|
466
477
|
throw Throwables.propagate(ex);
|
467
|
-
|
468
|
-
|
478
|
+
}
|
479
|
+
catch (FTPDataTransferException ex) {
|
469
480
|
log.info("FTP data transfer failed");
|
470
481
|
throw Throwables.propagate(ex);
|
471
|
-
|
472
|
-
|
482
|
+
}
|
483
|
+
catch (FTPAbortedException ex) {
|
473
484
|
log.info("FTP listing files failed");
|
474
485
|
throw Throwables.propagate(ex);
|
475
|
-
|
476
|
-
|
486
|
+
}
|
487
|
+
catch (FTPIllegalReplyException ex) {
|
477
488
|
log.info("FTP protocol error");
|
478
489
|
throw Throwables.propagate(ex);
|
479
|
-
|
480
|
-
|
490
|
+
}
|
491
|
+
catch (IOException ex) {
|
481
492
|
throw Throwables.propagate(ex);
|
482
|
-
|
483
|
-
|
493
|
+
}
|
494
|
+
finally {
|
484
495
|
try {
|
485
496
|
transfer.getWriterChannel().close();
|
486
|
-
}
|
497
|
+
}
|
498
|
+
catch (IOException ex) {
|
487
499
|
throw new RuntimeException(ex);
|
488
500
|
}
|
489
501
|
}
|
@@ -517,7 +529,7 @@ public class FtpFileInputPlugin
|
|
517
529
|
return retryExecutor()
|
518
530
|
.withRetryLimit(3)
|
519
531
|
.withInitialRetryWait(500)
|
520
|
-
.withMaxRetryWait(30*1000)
|
532
|
+
.withMaxRetryWait(30 * 1000)
|
521
533
|
.runInterruptible(new Retryable<InputStream>() {
|
522
534
|
@Override
|
523
535
|
public InputStream call() throws InterruptedIOException
|
@@ -537,10 +549,11 @@ public class FtpFileInputPlugin
|
|
537
549
|
throws RetryGiveupException
|
538
550
|
{
|
539
551
|
String message = String.format("FTP GET request failed. Retrying %d/%d after %d seconds. Message: %s",
|
540
|
-
retryCount, retryLimit, retryWait/1000, exception.getMessage());
|
552
|
+
retryCount, retryLimit, retryWait / 1000, exception.getMessage());
|
541
553
|
if (retryCount % 3 == 0) {
|
542
554
|
log.warn(message, exception);
|
543
|
-
}
|
555
|
+
}
|
556
|
+
else {
|
544
557
|
log.warn(message);
|
545
558
|
}
|
546
559
|
}
|
@@ -551,10 +564,12 @@ public class FtpFileInputPlugin
|
|
551
564
|
{
|
552
565
|
}
|
553
566
|
});
|
554
|
-
}
|
567
|
+
}
|
568
|
+
catch (RetryGiveupException ex) {
|
555
569
|
Throwables.propagateIfInstanceOf(ex.getCause(), IOException.class);
|
556
570
|
throw Throwables.propagate(ex.getCause());
|
557
|
-
}
|
571
|
+
}
|
572
|
+
catch (InterruptedException ex) {
|
558
573
|
throw new InterruptedIOException();
|
559
574
|
}
|
560
575
|
}
|
@@ -583,16 +598,18 @@ public class FtpFileInputPlugin
|
|
583
598
|
}
|
584
599
|
|
585
600
|
@Override
|
586
|
-
public
|
601
|
+
public InputStreamWithHints openNextWithHints() throws IOException
|
587
602
|
{
|
588
603
|
if (opened) {
|
589
604
|
return null;
|
590
605
|
}
|
591
606
|
opened = true;
|
592
607
|
|
593
|
-
return new
|
594
|
-
|
595
|
-
|
608
|
+
return new InputStreamWithHints(
|
609
|
+
new ResumableInputStream(
|
610
|
+
startDownload(log, client, path, 0L, executor),
|
611
|
+
new FtpInputStreamReopener(log, client, executor, path)), path
|
612
|
+
);
|
596
613
|
}
|
597
614
|
|
598
615
|
@Override
|
@@ -600,7 +617,8 @@ public class FtpFileInputPlugin
|
|
600
617
|
{
|
601
618
|
try {
|
602
619
|
executor.shutdownNow();
|
603
|
-
}
|
620
|
+
}
|
621
|
+
finally {
|
604
622
|
disconnectClient(client);
|
605
623
|
}
|
606
624
|
}
|
@@ -615,7 +633,9 @@ public class FtpFileInputPlugin
|
|
615
633
|
super(task.getBufferAllocator(), new SingleFileProvider(log, task, taskIndex));
|
616
634
|
}
|
617
635
|
|
618
|
-
public void abort()
|
636
|
+
public void abort()
|
637
|
+
{
|
638
|
+
}
|
619
639
|
|
620
640
|
public TaskReport commit()
|
621
641
|
{
|
@@ -1,5 +1,313 @@
|
|
1
1
|
package org.embulk.input;
|
2
2
|
|
3
|
+
import com.google.common.collect.ImmutableList;
|
4
|
+
import com.google.common.collect.ImmutableMap;
|
5
|
+
import com.google.common.collect.Lists;
|
6
|
+
import org.embulk.EmbulkTestRuntime;
|
7
|
+
import org.embulk.config.ConfigDiff;
|
8
|
+
import org.embulk.config.ConfigSource;
|
9
|
+
import org.embulk.config.TaskReport;
|
10
|
+
import org.embulk.config.TaskSource;
|
11
|
+
import org.embulk.input.FtpFileInputPlugin.PluginTask;
|
12
|
+
import org.embulk.spi.Exec;
|
13
|
+
import org.embulk.spi.FileInputPlugin;
|
14
|
+
import org.embulk.spi.FileInputRunner;
|
15
|
+
import org.embulk.spi.InputPlugin;
|
16
|
+
import org.embulk.spi.Schema;
|
17
|
+
import org.embulk.spi.TestPageBuilderReader;
|
18
|
+
import org.embulk.spi.TestPageBuilderReader.MockPageOutput;
|
19
|
+
import org.embulk.spi.util.Pages;
|
20
|
+
import org.embulk.standards.CsvParserPlugin;
|
21
|
+
import org.embulk.util.ftp.SSLPlugins;
|
22
|
+
import org.junit.Before;
|
23
|
+
import org.junit.BeforeClass;
|
24
|
+
import org.junit.Rule;
|
25
|
+
import org.junit.Test;
|
26
|
+
import org.slf4j.Logger;
|
27
|
+
|
28
|
+
import java.lang.reflect.Method;
|
29
|
+
import java.util.ArrayList;
|
30
|
+
import java.util.Arrays;
|
31
|
+
import java.util.List;
|
32
|
+
import java.util.Map;
|
33
|
+
|
34
|
+
import static org.hamcrest.CoreMatchers.is;
|
35
|
+
import static org.junit.Assert.assertThat;
|
36
|
+
|
3
37
|
public class TestFtpFileInputPlugin
|
4
38
|
{
|
39
|
+
private static String FTP_TEST_HOST;
|
40
|
+
private static Integer FTP_TEST_PORT;
|
41
|
+
private static Integer FTP_TEST_SSL_PORT;
|
42
|
+
private static String FTP_TEST_USER;
|
43
|
+
private static String FTP_TEST_PASSWORD;
|
44
|
+
private static String FTP_TEST_SSL_TRUSTED_CA_CERT_FILE;
|
45
|
+
private static String FTP_TEST_SSL_TRUSTED_CA_CERT_DATA;
|
46
|
+
private static String FTP_TEST_DIRECTORY;
|
47
|
+
private static String FTP_TEST_PATH_PREFIX;
|
48
|
+
private FileInputRunner runner;
|
49
|
+
private TestPageBuilderReader.MockPageOutput output;
|
50
|
+
|
51
|
+
/*
|
52
|
+
* This test case requires environment variables
|
53
|
+
* FTP_TEST_HOST
|
54
|
+
* FTP_TEST_USER
|
55
|
+
* FTP_TEST_PASSWORD
|
56
|
+
* FTP_TEST_SSL_TRUSTED_CA_CERT_FILE
|
57
|
+
*/
|
58
|
+
@BeforeClass
|
59
|
+
public static void initializeConstant()
|
60
|
+
{
|
61
|
+
final Map<String, String> env = System.getenv();
|
62
|
+
FTP_TEST_HOST = env.getOrDefault("FTP_TEST_HOST", "localhost");
|
63
|
+
FTP_TEST_PORT = Integer.valueOf(env.getOrDefault("FTP_TEST_PORT", "11021"));
|
64
|
+
FTP_TEST_SSL_PORT = Integer.valueOf(env.getOrDefault("FTP_TEST_SSL_PORT", "990"));
|
65
|
+
FTP_TEST_USER = env.getOrDefault("FTP_TEST_USER", "scott");
|
66
|
+
FTP_TEST_PASSWORD = env.getOrDefault("FTP_TEST_PASSWORD", "tiger");
|
67
|
+
FTP_TEST_SSL_TRUSTED_CA_CERT_FILE = env.getOrDefault("FTP_TEST_SSL_TRUSTED_CA_CERT_FILE", "dummy");
|
68
|
+
FTP_TEST_SSL_TRUSTED_CA_CERT_DATA = env.getOrDefault("FTP_TEST_SSL_TRUSTED_CA_CERT_DATA", "dummy");
|
69
|
+
|
70
|
+
FTP_TEST_DIRECTORY = getDirectory(env.getOrDefault("FTP_TEST_DIRECTORY", "/unittest/"));
|
71
|
+
FTP_TEST_PATH_PREFIX = FTP_TEST_DIRECTORY + "sample_";
|
72
|
+
}
|
73
|
+
|
74
|
+
@Rule
|
75
|
+
public EmbulkTestRuntime runtime = new EmbulkTestRuntime();
|
76
|
+
private FtpFileInputPlugin plugin;
|
77
|
+
|
78
|
+
@Before
|
79
|
+
public void createResources()
|
80
|
+
{
|
81
|
+
plugin = new FtpFileInputPlugin();
|
82
|
+
runner = new FileInputRunner(runtime.getInstance(FtpFileInputPlugin.class));
|
83
|
+
output = new MockPageOutput();
|
84
|
+
}
|
85
|
+
|
86
|
+
@Test(expected = RuntimeException.class) // TODO ConfigException should be thrown
|
87
|
+
public void testTransactionWithInvalidHost()
|
88
|
+
{
|
89
|
+
ConfigSource config = config().deepCopy()
|
90
|
+
.set("host", "non-exists.example.com");
|
91
|
+
|
92
|
+
runner.transaction(config, new Control());
|
93
|
+
}
|
94
|
+
|
95
|
+
@Test
|
96
|
+
public void testResume()
|
97
|
+
{
|
98
|
+
PluginTask task = config().loadConfig(PluginTask.class);
|
99
|
+
task.setSSLConfig(sslConfig(task));
|
100
|
+
task.setFiles(Arrays.asList("in/aa/a"));
|
101
|
+
ConfigDiff configDiff = plugin.resume(task.dump(), 0, new FileInputPlugin.Control()
|
102
|
+
{
|
103
|
+
@Override
|
104
|
+
public List<TaskReport> run(TaskSource taskSource, int taskCount)
|
105
|
+
{
|
106
|
+
return emptyTaskReports(taskCount);
|
107
|
+
}
|
108
|
+
});
|
109
|
+
assertThat(configDiff.get(String.class, "last_path"), is("in/aa/a"));
|
110
|
+
}
|
111
|
+
|
112
|
+
@Test
|
113
|
+
public void testCleanup()
|
114
|
+
{
|
115
|
+
PluginTask task = config().loadConfig(PluginTask.class);
|
116
|
+
plugin.cleanup(task.dump(), 0, Lists.<TaskReport>newArrayList()); // no errors happens
|
117
|
+
}
|
118
|
+
|
119
|
+
@Test
|
120
|
+
@SuppressWarnings("unchecked")
|
121
|
+
public void testListFilesWithNonExistPath() throws Exception
|
122
|
+
{
|
123
|
+
ConfigSource config = config().deepCopy()
|
124
|
+
.set("path_prefix", "non-exists-path");
|
125
|
+
PluginTask task = config.loadConfig(PluginTask.class);
|
126
|
+
plugin.transaction(config, new FileInputPlugin.Control() {
|
127
|
+
@Override
|
128
|
+
public List<TaskReport> run(TaskSource taskSource, int taskCount)
|
129
|
+
{
|
130
|
+
assertThat(taskCount, is(0));
|
131
|
+
return emptyTaskReports(taskCount);
|
132
|
+
}
|
133
|
+
});
|
134
|
+
|
135
|
+
Method method = FtpFileInputPlugin.class.getDeclaredMethod("listFiles", Logger.class, PluginTask.class);
|
136
|
+
method.setAccessible(true);
|
137
|
+
Logger logger = Exec.getLogger(FtpFileInputPlugin.class);
|
138
|
+
List<String> fileList = (List<String>) method.invoke(plugin, logger, task);
|
139
|
+
assertThat(fileList.size(), is(0));
|
140
|
+
}
|
141
|
+
|
142
|
+
@Test
|
143
|
+
@SuppressWarnings("unchecked")
|
144
|
+
public void testListFiles() throws Exception
|
145
|
+
{
|
146
|
+
List<String> expected = Arrays.asList(
|
147
|
+
FTP_TEST_PATH_PREFIX + "01.csv",
|
148
|
+
FTP_TEST_PATH_PREFIX + "02.csv"
|
149
|
+
);
|
150
|
+
|
151
|
+
ConfigSource config = config();
|
152
|
+
final PluginTask task = config.loadConfig(PluginTask.class);
|
153
|
+
ConfigDiff configDiff = plugin.transaction(config, new FileInputPlugin.Control() {
|
154
|
+
@Override
|
155
|
+
public List<TaskReport> run(TaskSource taskSource, int taskCount)
|
156
|
+
{
|
157
|
+
assertThat(taskCount, is(2));
|
158
|
+
return emptyTaskReports(taskCount);
|
159
|
+
}
|
160
|
+
});
|
161
|
+
|
162
|
+
Method method = FtpFileInputPlugin.class.getDeclaredMethod("listFiles", Logger.class, PluginTask.class);
|
163
|
+
method.setAccessible(true);
|
164
|
+
Logger logger = Exec.getLogger(FtpFileInputPlugin.class);
|
165
|
+
List<String> fileList = (List<String>) method.invoke(plugin, logger, task);
|
166
|
+
assertThat(fileList.get(0), is(expected.get(0)));
|
167
|
+
assertThat(fileList.get(1), is(expected.get(1)));
|
168
|
+
assertThat(configDiff.get(String.class, "last_path"), is(FTP_TEST_PATH_PREFIX + "02.csv"));
|
169
|
+
}
|
170
|
+
|
171
|
+
@Test
|
172
|
+
public void testListFilesByPrefixIncrementalFalse()
|
173
|
+
{
|
174
|
+
ConfigSource config = config()
|
175
|
+
.deepCopy()
|
176
|
+
.set("incremental", false);
|
177
|
+
|
178
|
+
ConfigDiff configDiff = runner.transaction(config, new Control());
|
179
|
+
|
180
|
+
assertThat(configDiff.toString(), is("{}"));
|
181
|
+
}
|
182
|
+
|
183
|
+
@Test
|
184
|
+
@SuppressWarnings("unchecked")
|
185
|
+
public void testFtpFileInputByOpen() throws Exception
|
186
|
+
{
|
187
|
+
ConfigSource config = config();
|
188
|
+
PluginTask task = config().loadConfig(PluginTask.class);
|
189
|
+
|
190
|
+
runner.transaction(config, new Control());
|
191
|
+
|
192
|
+
Method method = FtpFileInputPlugin.class.getDeclaredMethod("listFiles", Logger.class, PluginTask.class);
|
193
|
+
method.setAccessible(true);
|
194
|
+
Logger logger = Exec.getLogger(FtpFileInputPlugin.class);
|
195
|
+
List<String> fileList = (List<String>) method.invoke(plugin, logger, task);
|
196
|
+
task.setFiles(fileList);
|
197
|
+
|
198
|
+
assertRecords(config, output);
|
199
|
+
}
|
200
|
+
|
201
|
+
private static List<TaskReport> emptyTaskReports(int taskCount)
|
202
|
+
{
|
203
|
+
ImmutableList.Builder<TaskReport> reports = new ImmutableList.Builder<>();
|
204
|
+
for (int i = 0; i < taskCount; i++) {
|
205
|
+
reports.add(Exec.newTaskReport());
|
206
|
+
}
|
207
|
+
return reports.build();
|
208
|
+
}
|
209
|
+
|
210
|
+
private class Control
|
211
|
+
implements InputPlugin.Control
|
212
|
+
{
|
213
|
+
@Override
|
214
|
+
public List<TaskReport> run(TaskSource taskSource, Schema schema, int taskCount)
|
215
|
+
{
|
216
|
+
List<TaskReport> reports = new ArrayList<>();
|
217
|
+
for (int i = 0; i < taskCount; i++) {
|
218
|
+
reports.add(runner.run(taskSource, schema, i, output));
|
219
|
+
}
|
220
|
+
return reports;
|
221
|
+
}
|
222
|
+
}
|
223
|
+
|
224
|
+
private ConfigSource config()
|
225
|
+
{
|
226
|
+
return Exec.newConfigSource()
|
227
|
+
.set("host", FTP_TEST_HOST)
|
228
|
+
.set("port", FTP_TEST_PORT)
|
229
|
+
.set("user", FTP_TEST_USER)
|
230
|
+
.set("password", FTP_TEST_PASSWORD)
|
231
|
+
.set("path_prefix", FTP_TEST_PATH_PREFIX)
|
232
|
+
.set("last_path", "")
|
233
|
+
.set("file_ext", ".csv")
|
234
|
+
.set("max_connection_retry", 3)
|
235
|
+
.set("ssl", false)
|
236
|
+
.set("ssl_verify", false)
|
237
|
+
.set("ssl_verify_hostname", false)
|
238
|
+
.set("ssl_trusted_ca_cert_data", FTP_TEST_SSL_TRUSTED_CA_CERT_DATA)
|
239
|
+
.set("parser", parserConfig(schemaConfig()));
|
240
|
+
}
|
241
|
+
|
242
|
+
private ImmutableMap<String, Object> parserConfig(ImmutableList<Object> schemaConfig)
|
243
|
+
{
|
244
|
+
ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
|
245
|
+
builder.put("type", "csv");
|
246
|
+
builder.put("newline", "CRLF");
|
247
|
+
builder.put("delimiter", ",");
|
248
|
+
builder.put("quote", "\"");
|
249
|
+
builder.put("escape", "\"");
|
250
|
+
builder.put("trim_if_not_quoted", false);
|
251
|
+
builder.put("skip_header_lines", 1);
|
252
|
+
builder.put("allow_extra_columns", false);
|
253
|
+
builder.put("allow_optional_columns", false);
|
254
|
+
builder.put("columns", schemaConfig);
|
255
|
+
return builder.build();
|
256
|
+
}
|
257
|
+
|
258
|
+
private ImmutableList<Object> schemaConfig()
|
259
|
+
{
|
260
|
+
ImmutableList.Builder<Object> builder = new ImmutableList.Builder<>();
|
261
|
+
builder.add(ImmutableMap.of("name", "id", "type", "long"));
|
262
|
+
builder.add(ImmutableMap.of("name", "account", "type", "long"));
|
263
|
+
builder.add(ImmutableMap.of("name", "time", "type", "timestamp", "format", "%Y-%m-%d %H:%M:%S"));
|
264
|
+
builder.add(ImmutableMap.of("name", "purchase", "type", "timestamp", "format", "%Y%m%d"));
|
265
|
+
builder.add(ImmutableMap.of("name", "comment", "type", "string"));
|
266
|
+
return builder.build();
|
267
|
+
}
|
268
|
+
|
269
|
+
public SSLPlugins.SSLPluginConfig sslConfig(PluginTask task)
|
270
|
+
{
|
271
|
+
return SSLPlugins.configure(task);
|
272
|
+
}
|
273
|
+
|
274
|
+
private void assertRecords(ConfigSource config, MockPageOutput output)
|
275
|
+
{
|
276
|
+
List<Object[]> records = getRecords(config, output);
|
277
|
+
assertThat(records.size(), is(8));
|
278
|
+
{
|
279
|
+
Object[] record = records.get(0);
|
280
|
+
assertThat((long) record[0], is(1L));
|
281
|
+
assertThat((long) record[1], is(32864L));
|
282
|
+
assertThat(record[2].toString(), is("2015-01-27 19:23:49 UTC"));
|
283
|
+
assertThat(record[3].toString(), is("2015-01-27 00:00:00 UTC"));
|
284
|
+
assertThat(record[4].toString(), is("embulk"));
|
285
|
+
}
|
286
|
+
|
287
|
+
{
|
288
|
+
Object[] record = records.get(1);
|
289
|
+
assertThat((long) record[0], is(2L));
|
290
|
+
assertThat((long) record[1], is(14824L));
|
291
|
+
assertThat(record[2].toString(), is("2015-01-27 19:01:23 UTC"));
|
292
|
+
assertThat(record[3].toString(), is("2015-01-27 00:00:00 UTC"));
|
293
|
+
assertThat(record[4].toString(), is("embulk jruby"));
|
294
|
+
}
|
295
|
+
}
|
296
|
+
|
297
|
+
private List<Object[]> getRecords(ConfigSource config, MockPageOutput output)
|
298
|
+
{
|
299
|
+
Schema schema = config.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
|
300
|
+
return Pages.toObjects(schema, output.pages);
|
301
|
+
}
|
302
|
+
|
303
|
+
private static String getDirectory(String dir)
|
304
|
+
{
|
305
|
+
if (dir != null && !dir.endsWith("/")) {
|
306
|
+
dir = dir + "/";
|
307
|
+
}
|
308
|
+
if (dir.startsWith("/")) {
|
309
|
+
dir = dir.replaceFirst("/", "");
|
310
|
+
}
|
311
|
+
return dir;
|
312
|
+
}
|
5
313
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: embulk-input-ftp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-12-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -49,11 +49,13 @@ files:
|
|
49
49
|
- lib/embulk/input/ftp.rb
|
50
50
|
- src/main/java/org/embulk/input/FtpFileInputPlugin.java
|
51
51
|
- src/test/java/org/embulk/input/TestFtpFileInputPlugin.java
|
52
|
+
- src/test/resources/sample_01.csv
|
53
|
+
- src/test/resources/sample_02.csv
|
52
54
|
- classpath/bcpkix-jdk15on-1.52.jar
|
53
|
-
- classpath/embulk-
|
55
|
+
- classpath/embulk-util-ftp-0.2.0.jar
|
54
56
|
- classpath/bcprov-jdk15on-1.52.jar
|
55
57
|
- classpath/ftp4j-1.7.2.jar
|
56
|
-
- classpath/embulk-
|
58
|
+
- classpath/embulk-input-ftp-0.2.0.jar
|
57
59
|
homepage: https://github.com/embulk/embulk-input-ftp
|
58
60
|
licenses:
|
59
61
|
- Apache 2.0
|
Binary file
|
Binary file
|