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 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