embulk-input-ftp 0.1.6 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 00c4a9fb76f69fd4c8cc0364d0204286c8fc00c8
4
- data.tar.gz: 3c1c4545a7b51ae7832708b93af859fd12f5195a
3
+ metadata.gz: 27845469df3d11bb23cf8ef4a815733383a34768
4
+ data.tar.gz: 391a4e30104f40bb79d34b1780863bbac9fc32f0
5
5
  SHA512:
6
- metadata.gz: 7bb065f223aceb3519b07eaef2454f92dbd4832f795a5177fbb1a0fb5fcd28bb794fa59b28baead1c98b547e119737f0e3acec1dc2bf326731e590d333672c7c
7
- data.tar.gz: 45dc1c461576924388e9ee025ce9cc0a86536d07cba5e0daa69df11c152e767bc43002e291a69be2baaecaab2acb64ed076fbedad75192a9ff8599ca87face6b
6
+ metadata.gz: 43298f5fba9022feb85f0523f99e703f69553fb47c1f1e41a0a34c78584bb162ff84291de2117367e26dae290d865bc90122e806baa8a10b1a3833ea2a5f3d2a
7
+ data.tar.gz: 663e55ceb928b929b00e8c53566617932b45dcdda8d4cb81d1c2b5a67ba2ed99387fbdc1d6fcd03b6e82e89c6615bacd27eb5d561b536f4be789659b1bc3cfe6
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
- public String getPathPrefix();
64
+ String getPathPrefix();
62
65
 
63
66
  @Config("last_path")
64
67
  @ConfigDefault("null")
65
- public Optional<String> getLastPath();
68
+ Optional<String> getLastPath();
66
69
 
67
70
  @Config("incremental")
68
71
  @ConfigDefault("true")
69
- public boolean getIncremental();
72
+ boolean getIncremental();
70
73
 
71
74
  @Config("host")
72
- public String getHost();
75
+ String getHost();
73
76
 
74
77
  @Config("port")
75
78
  @ConfigDefault("null")
76
- public Optional<Integer> getPort();
79
+ Optional<Integer> getPort();
77
80
 
78
81
  @Config("user")
79
82
  @ConfigDefault("null")
80
- public Optional<String> getUser();
83
+ Optional<String> getUser();
81
84
 
82
85
  @Config("password")
83
86
  @ConfigDefault("null")
84
- public Optional<String> getPassword();
87
+ Optional<String> getPassword();
85
88
 
86
89
  @Config("passive_mode")
87
90
  @ConfigDefault("true")
88
- public boolean getPassiveMode();
91
+ boolean getPassiveMode();
89
92
 
90
93
  @Config("ascii_mode")
91
94
  @ConfigDefault("false")
92
- public boolean getAsciiMode();
95
+ boolean getAsciiMode();
93
96
 
94
97
  @Config("ssl")
95
98
  @ConfigDefault("false")
96
- public boolean getSsl();
99
+ boolean getSsl();
97
100
 
98
101
  @Config("ssl_explicit")
99
102
  @ConfigDefault("true")
100
- public boolean getSslExplicit();
103
+ boolean getSslExplicit();
101
104
 
102
- public List<String> getFiles();
103
- public void setFiles(List<String> files);
105
+ List<String> getFiles();
106
+ void setFiles(List<String> files);
104
107
 
105
- public SSLPluginConfig getSSLConfig();
106
- public void setSSLConfig(SSLPluginConfig config);
108
+ SSLPluginConfig getSSLConfig();
109
+ void setSSLConfig(SSLPluginConfig config);
107
110
 
108
111
  @ConfigInject
109
- public BufferAllocator getBufferAllocator();
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
- } else {
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().or(""));
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
- } else {
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
- } catch (FTPException ex) {
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
- } catch (FTPIllegalReplyException ex) {
240
+ }
241
+ catch (FTPIllegalReplyException ex) {
237
242
  log.info("FTP protocol error");
238
243
  throw Throwables.propagate(ex);
239
-
240
- } catch (IOException ex) {
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
- } finally {
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
- } catch (FTPException ex) {
261
+ }
262
+ catch (FTPException ex) {
257
263
  // do nothing
258
- } catch (FTPIllegalReplyException ex) {
264
+ }
265
+ catch (FTPIllegalReplyException ex) {
259
266
  // do nothing
260
- } catch (IOException ex) {
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
- } finally {
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
- } else {
293
+ }
294
+ else {
285
295
  int pos = prefix.lastIndexOf("/");
286
296
  if (pos < 0) {
287
297
  directory = "";
288
298
  fileNamePrefix = prefix;
289
- } else {
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
- } catch (FTPListParseException ex) {
322
+ }
323
+ catch (FTPListParseException ex) {
313
324
  log.info("FTP listing files failed");
314
325
  throw Throwables.propagate(ex);
315
-
316
- } catch (FTPAbortedException ex) {
326
+ }
327
+ catch (FTPAbortedException ex) {
317
328
  log.info("FTP listing files failed");
318
329
  throw Throwables.propagate(ex);
319
-
320
- } catch (FTPDataTransferException ex) {
330
+ }
331
+ catch (FTPDataTransferException ex) {
321
332
  log.info("FTP data transfer failed");
322
333
  throw Throwables.propagate(ex);
323
-
324
- } catch (FTPException ex) {
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
- } catch (FTPIllegalReplyException ex) {
338
+ }
339
+ catch (FTPIllegalReplyException ex) {
329
340
  log.info("FTP protocol error");
330
341
  throw Throwables.propagate(ex);
331
-
332
- } catch (IOException ex) {
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
- } catch (FTPException ex) {
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
- } catch (FTPDataTransferException ex) {
478
+ }
479
+ catch (FTPDataTransferException ex) {
469
480
  log.info("FTP data transfer failed");
470
481
  throw Throwables.propagate(ex);
471
-
472
- } catch (FTPAbortedException ex) {
482
+ }
483
+ catch (FTPAbortedException ex) {
473
484
  log.info("FTP listing files failed");
474
485
  throw Throwables.propagate(ex);
475
-
476
- } catch (FTPIllegalReplyException ex) {
486
+ }
487
+ catch (FTPIllegalReplyException ex) {
477
488
  log.info("FTP protocol error");
478
489
  throw Throwables.propagate(ex);
479
-
480
- } catch (IOException ex) {
490
+ }
491
+ catch (IOException ex) {
481
492
  throw Throwables.propagate(ex);
482
-
483
- } finally {
493
+ }
494
+ finally {
484
495
  try {
485
496
  transfer.getWriterChannel().close();
486
- } catch (IOException ex) {
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
- } else {
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
- } catch (RetryGiveupException ex) {
567
+ }
568
+ catch (RetryGiveupException ex) {
555
569
  Throwables.propagateIfInstanceOf(ex.getCause(), IOException.class);
556
570
  throw Throwables.propagate(ex.getCause());
557
- } catch (InterruptedException ex) {
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 InputStream openNext() throws IOException
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 ResumableInputStream(
594
- startDownload(log, client, path, 0L, executor),
595
- new FtpInputStreamReopener(log, client, executor, path));
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
- } finally {
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
  }
@@ -0,0 +1,5 @@
1
+ id,account,time,purchase,comment
2
+ 1,32864,2015-01-27 19:23:49,20150127,embulk
3
+ 2,14824,2015-01-27 19:01:23,20150127,embulk jruby
4
+ 3,27559,2015-01-28 02:20:02,20150128,"Embulk ""csv"" parser plugin"
5
+ 4,11270,2015-01-29 11:54:36,20150129,NULL
@@ -0,0 +1,5 @@
1
+ id,account,time,purchase,comment
2
+ 1,32864,2015-01-27 19:23:49,20150127,embulk
3
+ 2,14824,2015-01-27 19:01:23,20150127,embulk jruby
4
+ 3,27559,2015-01-28 02:20:02,20150128,"Embulk ""csv"" parser plugin"
5
+ 4,11270,2015-01-29 11:54:36,20150129,NULL
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.1.6
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-06-29 00:00:00.000000000 Z
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-input-ftp-0.1.6.jar
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-util-ftp-0.1.6.jar
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