embulk-output-ftp 0.1.0 → 0.1.1

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: fd1fb5019e704b0db01af96b695ee892fb042c52
4
- data.tar.gz: 963e27d5c2604becb7aa4fed07876939f106b0a9
3
+ metadata.gz: db87448f5b289f68ebb6c9d03b36f295a0139ac0
4
+ data.tar.gz: ab5abab07a3bec49eed5ec362c8fdec549564d88
5
5
  SHA512:
6
- metadata.gz: ae8ca510952cfe8a2517101ee844a895465e7f2588dccbb795b17cb7ec5ba8f6a350156e99cc0399a9faf54a372bf0db47f3ab96ced3f8eb519323998ff724ae
7
- data.tar.gz: 79be6833288b0c5ae9da3319430c966d43c38fa42762f70c6e8d5de3ffc24a14b0712b60e5fbac842e5e715814f06c9518fc402d035392b5ed6df243dca17291
6
+ metadata.gz: 7281539145640359c6c084c031b7a212efd3044755e57ea5b823f5a4b80635facf0c0f300e59fdf1ced7db7425104a0fdd0f737de7274674a0fba93987b4d8b5
7
+ data.tar.gz: a2d49e39e67f7c5c680e62651db9ccf6e845bc42e317d81a834d52e9c83a3630003b10667f304fc4351e637f74bb9304b985510d688d5b39db86d837b89eb4de
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.1.1 - 2016-07-20
2
+
3
+ * [maintenance] Add unit test, refactoring, change logging logic
4
+
1
5
  ## 0.1.0 - 2016-07-20
2
6
 
3
7
  * [new feature] First Release
data/README.md CHANGED
@@ -37,7 +37,7 @@ out:
37
37
  path_prefix: /ftp/file/path/prefix
38
38
  ext: csv
39
39
  formatter:
40
- type: csv
40
+ type: csv.gz
41
41
  header_line: false
42
42
  encoders:
43
43
  - {type: gzip}
@@ -87,7 +87,7 @@ out:
87
87
  -----END CERTIFICATE-----
88
88
 
89
89
  path_prefix: /ftp/file/path/prefix
90
- ext: csv
90
+ ext: csv
91
91
  ```
92
92
 
93
93
 
@@ -96,3 +96,57 @@ out:
96
96
  ```
97
97
  $ ./gradlew gem # -t to watch change of files and rebuild continuously
98
98
  ```
99
+
100
+ ## Test
101
+
102
+ ```
103
+ $ ./gradlew test # -t to watch change of files and rebuild continuously
104
+ ```
105
+
106
+ To run unit tests, we need to configure the following environment variables.
107
+
108
+ When environment variables are not set, skip some test cases.
109
+
110
+ ```
111
+ FTP_TEST_HOST
112
+ FTP_TEST_USER
113
+ FTP_TEST_PASSWORD
114
+ FTP_TEST_SSL_TRUSTED_CA_CERT_FILE
115
+ FTP_TEST_SSL_TRUSTED_CA_CERT_DATA
116
+ ```
117
+
118
+ If you're using Mac OS X El Capitan and GUI Applications(IDE), like as follows.
119
+ ```xml
120
+ $ vi ~/Library/LaunchAgents/environment.plist
121
+ <?xml version="1.0" encoding="UTF-8"?>
122
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
123
+ <plist version="1.0">
124
+ <dict>
125
+ <key>Label</key>
126
+ <string>my.startup</string>
127
+ <key>ProgramArguments</key>
128
+ <array>
129
+ <string>sh</string>
130
+ <string>-c</string>
131
+ <string>
132
+ launchctl setenv FTP_TEST_HOST ftp.example.com
133
+ launchctl setenv FTP_TEST_USER username
134
+ launchctl setenv FTP_TEST_PASSWORD password
135
+ launchctl setenv FTP_TEST_SSL_TRUSTED_CA_CERT_FILE /path/to/cert.pem
136
+ launchctl setenv FTP_TEST_SSL_TRUSTED_CA_CERT_DATA /path/to/cert.pem
137
+ </string>
138
+ </array>
139
+ <key>RunAtLoad</key>
140
+ <true/>
141
+ </dict>
142
+ </plist>
143
+
144
+ $ launchctl load ~/Library/LaunchAgents/environment.plist
145
+ $ launchctl getenv FTP_TEST_HOST //try to get value.
146
+
147
+ Then start your applications.
148
+ ```
149
+
150
+ ## Acknowledgement
151
+
152
+ This program is forked from [embulk-input-ftp](https://github.com/embulk/embulk-input-ftp) and originally written by @frsyuki, modified by @sakama.
data/build.gradle CHANGED
@@ -14,7 +14,7 @@ configurations {
14
14
  provided
15
15
  }
16
16
 
17
- version = "0.1.0"
17
+ version = "0.1.1"
18
18
 
19
19
  sourceCompatibility = 1.7
20
20
  targetCompatibility = 1.7
@@ -25,6 +25,8 @@ dependencies {
25
25
  compile files("libs/ftp4j-1.7.2.jar")
26
26
  compile "org.bouncycastle:bcpkix-jdk15on:1.52"
27
27
  testCompile "junit:junit:4.+"
28
+ testCompile "org.embulk:embulk-core:0.8.9:tests"
29
+ testCompile "org.embulk:embulk-standards:0.8.9"
28
30
  }
29
31
 
30
32
  task classpath(type: Copy, dependsOn: ["jar"]) {
@@ -92,7 +92,9 @@
92
92
  <module name="TypeName"/>
93
93
  <module name="PackageName"/>
94
94
  <module name="ParameterName"/>
95
- <module name="StaticVariableName"/>
95
+ <module name="StaticVariableName">
96
+ <property name="format" value="^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"/>
97
+ </module>
96
98
  <module name="ClassTypeParameterName">
97
99
  <property name="format" value="^[A-Z][0-9]?$"/>
98
100
  </module>
@@ -82,9 +82,8 @@
82
82
  <module name="TypeName"/>
83
83
  <module name="PackageName"/>
84
84
  <module name="ParameterName"/>
85
- <module name="StaticVariableName"/>
86
- <module name="ClassTypeParameterName">
87
- <property name="format" value="^[A-Z][0-9]?$"/>
85
+ <module name="StaticVariableName">
86
+ <property name="format" value="^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"/>
88
87
  </module>
89
88
  <module name="MethodTypeParameterName">
90
89
  <property name="format" value="^[A-Z][0-9]?$"/>
@@ -103,7 +103,7 @@ public class FtpFileOutputPlugin implements FileOutputPlugin
103
103
  client = newFTPClient(log, task);
104
104
  }
105
105
  catch (Exception ex) {
106
- throw new ConfigException(ex);
106
+ throw new ConfigException("Faild to connect to FTP server", ex);
107
107
  }
108
108
  finally {
109
109
  disconnectClient(client);
@@ -168,8 +168,8 @@ public class FtpFileOutputPlugin implements FileOutputPlugin
168
168
  suffix = "." + suffix;
169
169
  }
170
170
  filePath = pathPrefix + String.format(sequenceFormat, taskIndex, fileIndex) + suffix;
171
- file = File.createTempFile(filePath, ".tmp");
172
- log.info("Writing local file {}", file.getAbsolutePath());
171
+ file = Exec.getTempFileSpace().createTempFile(filePath, ".tmp");
172
+ log.info("Writing local temporary file \"{}\"", file.getAbsolutePath());
173
173
  output = new BufferedOutputStream(new FileOutputStream(file));
174
174
  }
175
175
  catch (IOException ex) {
@@ -225,13 +225,14 @@ public class FtpFileOutputPlugin implements FileOutputPlugin
225
225
  public Void call() throws FTPIllegalReplyException, FTPException, FTPDataTransferException,
226
226
  FTPAbortedException, IOException, RetryGiveupException
227
227
  {
228
- log.info("Upload start {} to {}", file.getAbsolutePath(), filePath);
229
- client.upload(filePath, new BufferedInputStream(new FileInputStream(file)), 0L, 0L, new LoggingTransferListener(log, TRANSFER_NOTICE_BYTES));
230
- log.info("Upload completed {} to {}", file.getAbsolutePath(), filePath);
228
+ client.upload(filePath,
229
+ new BufferedInputStream(new FileInputStream(file)), 0L, 0L,
230
+ new LoggingTransferListener(file.getAbsolutePath(), filePath, log, TRANSFER_NOTICE_BYTES)
231
+ );
231
232
  if (!file.delete()) {
232
233
  throw new ConfigException("Couldn't delete local file " + file.getAbsolutePath());
233
234
  }
234
- log.info("Delete local temporary file {}", file.getAbsolutePath());
235
+ log.info("Deleted local temporary file \"{}\"", file.getAbsolutePath());
235
236
  return null;
236
237
  }
237
238
 
@@ -396,7 +397,7 @@ public class FtpFileOutputPlugin implements FileOutputPlugin
396
397
 
397
398
  public void received(String statement)
398
399
  {
399
- log.info("< " + statement);
400
+ log.debug("< " + statement);
400
401
  }
401
402
 
402
403
  public void sent(String statement)
@@ -405,20 +406,24 @@ public class FtpFileOutputPlugin implements FileOutputPlugin
405
406
  // don't show password
406
407
  return;
407
408
  }
408
- log.info("> {}", statement);
409
+ log.debug("> {}", statement);
409
410
  }
410
411
  }
411
412
 
412
413
  private static class LoggingTransferListener implements FTPDataTransferListener
413
414
  {
415
+ private final String localPath;
416
+ private final String remotePath;
414
417
  private final Logger log;
415
418
  private final long transferNoticeBytes;
416
419
 
417
420
  private long totalTransfer;
418
421
  private long nextTransferNotice;
419
422
 
420
- public LoggingTransferListener(Logger log, long transferNoticeBytes)
423
+ public LoggingTransferListener(String localPath, String remotePath, Logger log, long transferNoticeBytes)
421
424
  {
425
+ this.localPath = localPath;
426
+ this.remotePath = remotePath;
422
427
  this.log = log;
423
428
  this.transferNoticeBytes = transferNoticeBytes;
424
429
  this.nextTransferNotice = transferNoticeBytes;
@@ -426,7 +431,7 @@ public class FtpFileOutputPlugin implements FileOutputPlugin
426
431
 
427
432
  public void started()
428
433
  {
429
- log.info("Transfer started");
434
+ log.info("Transfer started. local path:\"{}\" remote path:\"{}\"", localPath, remotePath);
430
435
  }
431
436
 
432
437
  public void transferred(int length)
@@ -440,7 +445,7 @@ public class FtpFileOutputPlugin implements FileOutputPlugin
440
445
 
441
446
  public void completed()
442
447
  {
443
- log.info("Transfer completed {} bytes", totalTransfer);
448
+ log.info("Transfer completed. remote path:\"{}\", size:{} bytes", remotePath, totalTransfer);
444
449
  }
445
450
 
446
451
  public void aborted()
@@ -63,7 +63,7 @@ public class SSLPlugins
63
63
 
64
64
  public static class SSLPluginConfig
65
65
  {
66
- static SSLPluginConfig noVerify = new SSLPluginConfig(VerifyMode.NO_VERIFY, false, ImmutableList.<byte[]>of());
66
+ static SSLPluginConfig NO_VERIFY = new SSLPluginConfig(VerifyMode.NO_VERIFY, false, ImmutableList.<byte[]>of());
67
67
 
68
68
  private final VerifyMode verifyMode;
69
69
  private final boolean verifyHostname;
@@ -176,7 +176,7 @@ public class SSLPlugins
176
176
  }
177
177
  }
178
178
  else {
179
- return SSLPluginConfig.noVerify;
179
+ return SSLPluginConfig.NO_VERIFY;
180
180
  }
181
181
  }
182
182
 
@@ -1,5 +1,375 @@
1
1
  package org.embulk.output.ftp;
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 com.google.common.io.Resources;
7
+ import it.sauronsoftware.ftp4j.FTPClient;
8
+ import org.embulk.EmbulkTestRuntime;
9
+ import org.embulk.config.ConfigDiff;
10
+ import org.embulk.config.ConfigException;
11
+ import org.embulk.config.ConfigSource;
12
+ import org.embulk.config.TaskReport;
13
+ import org.embulk.config.TaskSource;
14
+ import org.embulk.output.ftp.FtpFileOutputPlugin.PluginTask;
15
+ import org.embulk.spi.Buffer;
16
+ import org.embulk.spi.Exec;
17
+ import org.embulk.spi.FileOutputPlugin;
18
+ import org.embulk.spi.FileOutputRunner;
19
+ import org.embulk.spi.OutputPlugin;
20
+ import org.embulk.spi.Schema;
21
+ import org.embulk.spi.TransactionalFileOutput;
22
+ import org.embulk.standards.CsvParserPlugin;
23
+
24
+ import org.junit.Before;
25
+ import org.junit.BeforeClass;
26
+ import org.junit.Rule;
27
+ import org.junit.Test;
28
+ import org.slf4j.Logger;
29
+
30
+ import static org.junit.Assert.assertEquals;
31
+ import static org.junit.Assume.assumeNotNull;
32
+
33
+ import java.io.BufferedInputStream;
34
+ import java.io.BufferedReader;
35
+ import java.io.ByteArrayOutputStream;
36
+ import java.io.File;
37
+ import java.io.FileInputStream;
38
+ import java.io.IOException;
39
+ import java.io.InputStream;
40
+ import java.io.InputStreamReader;
41
+ import java.lang.reflect.Method;
42
+ import java.security.GeneralSecurityException;
43
+ import java.util.Arrays;
44
+ import java.util.List;
45
+
3
46
  public class TestFtpFileOutputPlugin
4
47
  {
48
+ private static String FTP_TEST_HOST;
49
+ private static String FTP_TEST_USER;
50
+ private static String FTP_TEST_PASSWORD;
51
+ private static String FTP_TEST_SSL_TRUSTED_CA_CERT_FILE;
52
+ private static String FTP_TEST_DIRECTORY;
53
+ private static String FTP_TEST_PATH_PREFIX;
54
+ private static String LOCAL_PATH_PREFIX;
55
+ private FileOutputRunner runner;
56
+
57
+ /*
58
+ * This test case requires environment variables
59
+ * FTP_TEST_HOST
60
+ * FTP_TEST_USER
61
+ * FTP_TEST_PASSWORD
62
+ * FTP_TEST_SSL_TRUSTED_CA_CERT_FILE
63
+ */
64
+ @BeforeClass
65
+ public static void initializeConstant()
66
+ {
67
+ FTP_TEST_HOST = System.getenv("FTP_TEST_HOST");
68
+ FTP_TEST_USER = System.getenv("FTP_TEST_USER");
69
+ FTP_TEST_PASSWORD = System.getenv("FTP_TEST_PASSWORD");
70
+ FTP_TEST_SSL_TRUSTED_CA_CERT_FILE = System.getenv("FTP_TEST_SSL_TRUSTED_CA_CERT_FILE");
71
+ // skip test cases, if environment variables are not set.
72
+ assumeNotNull(FTP_TEST_HOST, FTP_TEST_USER, FTP_TEST_PASSWORD, FTP_TEST_SSL_TRUSTED_CA_CERT_FILE);
73
+
74
+ FTP_TEST_DIRECTORY = System.getenv("FTP_TEST_DIRECTORY") != null ? getDirectory(System.getenv("FTP_TEST_DIRECTORY")) : getDirectory("/unittest/");
75
+ FTP_TEST_PATH_PREFIX = FTP_TEST_DIRECTORY + "sample_";
76
+ LOCAL_PATH_PREFIX = Resources.getResource("sample_01.csv").getPath();
77
+ }
78
+
79
+ @Rule
80
+ public EmbulkTestRuntime runtime = new EmbulkTestRuntime();
81
+ private FtpFileOutputPlugin plugin;
82
+
83
+ @Before
84
+ public void createResources() throws GeneralSecurityException, NoSuchMethodException, IOException
85
+ {
86
+ plugin = new FtpFileOutputPlugin();
87
+ runner = new FileOutputRunner(runtime.getInstance(FtpFileOutputPlugin.class));
88
+ }
89
+
90
+ @Test
91
+ public void checkDefaultValues()
92
+ {
93
+ ConfigSource config = Exec.newConfigSource()
94
+ .set("in", inputConfig())
95
+ .set("parser", parserConfig(schemaConfig()))
96
+ .set("type", "ftp")
97
+ .set("host", FTP_TEST_HOST)
98
+ .set("user", FTP_TEST_USER)
99
+ .set("password", FTP_TEST_PASSWORD)
100
+ .set("path_prefix", "my-prefix")
101
+ .set("file_ext", ".csv")
102
+ .set("formatter", formatterConfig());
103
+
104
+ PluginTask task = config.loadConfig(PluginTask.class);
105
+
106
+ assertEquals(FTP_TEST_HOST, task.getHost());
107
+ assertEquals(FTP_TEST_USER, task.getUser().get());
108
+ assertEquals(FTP_TEST_PASSWORD, task.getPassword().get());
109
+ assertEquals(10, task.getMaxConnectionRetry());
110
+ }
111
+
112
+ @Test
113
+ public void testTransaction()
114
+ {
115
+ ConfigSource config = Exec.newConfigSource()
116
+ .set("in", inputConfig())
117
+ .set("parser", parserConfig(schemaConfig()))
118
+ .set("type", "ftp")
119
+ .set("host", FTP_TEST_HOST)
120
+ .set("user", FTP_TEST_USER)
121
+ .set("password", FTP_TEST_PASSWORD)
122
+ .set("path_prefix", "my-prefix")
123
+ .set("file_ext", ".csv")
124
+ .set("formatter", formatterConfig());
125
+
126
+ Schema schema = config.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
127
+
128
+ runner.transaction(config, schema, 0, new Control());
129
+ }
130
+
131
+ @Test(expected = ConfigException.class)
132
+ public void testTransactionWithInvalidHost()
133
+ {
134
+ ConfigSource config = Exec.newConfigSource()
135
+ .set("in", inputConfig())
136
+ .set("parser", parserConfig(schemaConfig()))
137
+ .set("type", "ftp")
138
+ .set("host", "non-exists.example.com")
139
+ .set("user", FTP_TEST_USER)
140
+ .set("password", FTP_TEST_PASSWORD)
141
+ .set("path_prefix", "my-prefix")
142
+ .set("file_ext", ".csv")
143
+ .set("formatter", formatterConfig());
144
+
145
+ Schema schema = config.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
146
+
147
+ runner.transaction(config, schema, 0, new Control());
148
+ }
149
+
150
+ // @Test
151
+ // public void testTransactionWithSsl()
152
+ // {
153
+ // ConfigSource config = Exec.newConfigSource()
154
+ // .set("in", inputConfig())
155
+ // .set("parser", parserConfig(schemaConfig()))
156
+ // .set("type", "ftp")
157
+ // .set("host", FTP_TEST_HOST)
158
+ // .set("port", 990)
159
+ // .set("user", FTP_TEST_USER)
160
+ // .set("password", FTP_TEST_PASSWORD)
161
+ // .set("ssl", true)
162
+ // .set("ssl_verify", false)
163
+ // .set("ssl_verify_hostname", false)
164
+ // .set("ssl_trusted_ca_cert_file", FTP_TEST_SSL_TRUSTED_CA_CERT_FILE)
165
+ // .set("path_prefix", "my-prefix")
166
+ // .set("file_ext", ".csv")
167
+ // .set("formatter", formatterConfig());
168
+ //
169
+ // Schema schema = config.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
170
+ //
171
+ // runner.transaction(config, schema, 0, new Control());
172
+ // }
173
+
174
+ @Test
175
+ public void testResume()
176
+ {
177
+ PluginTask task = config().loadConfig(PluginTask.class);
178
+ ConfigDiff configDiff = plugin.resume(task.dump(), 0, new FileOutputPlugin.Control()
179
+ {
180
+ @Override
181
+ public List<TaskReport> run(TaskSource taskSource)
182
+ {
183
+ return Lists.newArrayList(Exec.newTaskReport());
184
+ }
185
+ });
186
+ //assertEquals("in/aa/a", configDiff.get(String.class, "last_path"));
187
+ }
188
+
189
+ @Test
190
+ public void testCleanup()
191
+ {
192
+ PluginTask task = config().loadConfig(PluginTask.class);
193
+ plugin.cleanup(task.dump(), 0, Lists.<TaskReport>newArrayList()); // no errors happens
194
+ }
195
+
196
+ @Test
197
+ public void testFtpFileOutputByOpen() throws Exception
198
+ {
199
+ ConfigSource configSource = config();
200
+ PluginTask task = configSource.loadConfig(PluginTask.class);
201
+ task.setSSLConfig(SSLPlugins.configure(task));
202
+ Schema schema = configSource.getNested("parser").loadConfig(CsvParserPlugin.PluginTask.class).getSchemaConfig().toSchema();
203
+ runner.transaction(configSource, schema, 0, new Control());
204
+
205
+ TransactionalFileOutput output = plugin.open(task.dump(), 0);
206
+
207
+ output.nextFile();
208
+
209
+ FileInputStream is = new FileInputStream(LOCAL_PATH_PREFIX);
210
+ byte[] bytes = convertInputStreamToByte(is);
211
+ Buffer buffer = Buffer.wrap(bytes);
212
+ output.add(buffer);
213
+
214
+ output.finish();
215
+ output.commit();
216
+
217
+ String remotePath = FTP_TEST_PATH_PREFIX + String.format(task.getSequenceFormat(), 0, 0) + task.getFileNameExtension();
218
+ assertRecords(remotePath, task);
219
+ }
220
+
221
+ public ConfigSource config()
222
+ {
223
+ return Exec.newConfigSource()
224
+ .set("in", inputConfig())
225
+ .set("parser", parserConfig(schemaConfig()))
226
+ .set("type", "ftp")
227
+ .set("host", FTP_TEST_HOST)
228
+ .set("user", FTP_TEST_USER)
229
+ .set("password", FTP_TEST_PASSWORD)
230
+ .set("path_prefix", FTP_TEST_PATH_PREFIX)
231
+ .set("last_path", "")
232
+ .set("file_ext", ".csv")
233
+ .set("formatter", formatterConfig());
234
+ }
235
+
236
+ private class Control implements OutputPlugin.Control
237
+ {
238
+ @Override
239
+ public List<TaskReport> run(TaskSource taskSource)
240
+ {
241
+ return Lists.newArrayList(Exec.newTaskReport());
242
+ }
243
+ }
244
+
245
+ private ImmutableMap<String, Object> inputConfig()
246
+ {
247
+ ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
248
+ builder.put("type", "file");
249
+ builder.put("path_prefix", LOCAL_PATH_PREFIX);
250
+ builder.put("last_path", "");
251
+ return builder.build();
252
+ }
253
+
254
+ private ImmutableMap<String, Object> parserConfig(ImmutableList<Object> schemaConfig)
255
+ {
256
+ ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
257
+ builder.put("type", "csv");
258
+ builder.put("newline", "CRLF");
259
+ builder.put("delimiter", ",");
260
+ builder.put("quote", "\"");
261
+ builder.put("escape", "\"");
262
+ builder.put("trim_if_not_quoted", false);
263
+ builder.put("skip_header_lines", 1);
264
+ builder.put("allow_extra_columns", false);
265
+ builder.put("allow_optional_columns", false);
266
+ builder.put("columns", schemaConfig);
267
+ return builder.build();
268
+ }
269
+
270
+ private ImmutableList<Object> schemaConfig()
271
+ {
272
+ ImmutableList.Builder<Object> builder = new ImmutableList.Builder<>();
273
+ builder.add(ImmutableMap.of("name", "id", "type", "long"));
274
+ builder.add(ImmutableMap.of("name", "account", "type", "long"));
275
+ builder.add(ImmutableMap.of("name", "time", "type", "timestamp", "format", "%Y-%m-%d %H:%M:%S"));
276
+ builder.add(ImmutableMap.of("name", "purchase", "type", "timestamp", "format", "%Y%m%d"));
277
+ builder.add(ImmutableMap.of("name", "comment", "type", "string"));
278
+ builder.add(ImmutableMap.of("name", "json_column", "type", "json"));
279
+ return builder.build();
280
+ }
281
+
282
+ private ImmutableMap<String, Object> formatterConfig()
283
+ {
284
+ ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
285
+ builder.put("type", "csv");
286
+ builder.put("header_line", "false");
287
+ builder.put("timezone", "Asia/Tokyo");
288
+ return builder.build();
289
+ }
290
+
291
+ private void assertRecords(String remotePath, PluginTask task) throws Exception
292
+ {
293
+ ImmutableList<List<String>> records = getFileContentsFromFtp(remotePath, task);
294
+ assertEquals(6, records.size());
295
+ {
296
+ List<String> record = records.get(1);
297
+ assertEquals("1", record.get(0));
298
+ assertEquals("32864", record.get(1));
299
+ assertEquals("2015-01-27 19:23:49", record.get(2));
300
+ assertEquals("20150127", record.get(3));
301
+ assertEquals("embulk", record.get(4));
302
+ assertEquals("{\"k\":true}", record.get(5));
303
+ }
304
+
305
+ {
306
+ List<String> record = records.get(2);
307
+ assertEquals("2", record.get(0));
308
+ assertEquals("14824", record.get(1));
309
+ assertEquals("2015-01-27 19:01:23", record.get(2));
310
+ assertEquals("20150127", record.get(3));
311
+ assertEquals("embulk jruby", record.get(4));
312
+ assertEquals("{\"k\":1}", record.get(5));
313
+ }
314
+
315
+ {
316
+ List<String> record = records.get(3);
317
+ assertEquals("{\"k\":1.23}", record.get(5));
318
+ }
319
+
320
+ {
321
+ List<String> record = records.get(4);
322
+ assertEquals("{\"k\":\"v\"}", record.get(5));
323
+ }
324
+
325
+ {
326
+ List<String> record = records.get(5);
327
+ assertEquals("{\"k\":\"2015-02-03 08:13:45\"}", record.get(5));
328
+ }
329
+ }
330
+
331
+ private ImmutableList<List<String>> getFileContentsFromFtp(String path, PluginTask task) throws Exception
332
+ {
333
+ Method method = FtpFileOutputPlugin.class.getDeclaredMethod("newFTPClient", Logger.class, PluginTask.class);
334
+ method.setAccessible(true);
335
+ FTPClient client = (FTPClient) method.invoke(plugin, Exec.getLogger(FtpFileOutputPlugin.class), task);
336
+
337
+ ImmutableList.Builder<List<String>> builder = new ImmutableList.Builder<>();
338
+ File localFile = Exec.getTempFileSpace().createTempFile();
339
+ client.download(path, localFile);
340
+ InputStream is = new BufferedInputStream(new FileInputStream(localFile));
341
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
342
+ String line;
343
+ while ((line = reader.readLine()) != null) {
344
+ List<String> records = Arrays.asList(line.split(",", 0));
345
+
346
+ builder.add(records);
347
+ }
348
+ return builder.build();
349
+ }
350
+
351
+ private static String getDirectory(String dir)
352
+ {
353
+ if (!dir.isEmpty() && !dir.endsWith("/")) {
354
+ dir = dir + "/";
355
+ }
356
+ if (dir.startsWith("/")) {
357
+ dir = dir.replaceFirst("/", "");
358
+ }
359
+ return dir;
360
+ }
361
+
362
+ private byte[] convertInputStreamToByte(InputStream is) throws IOException
363
+ {
364
+ ByteArrayOutputStream bo = new ByteArrayOutputStream();
365
+ byte [] buffer = new byte[1024];
366
+ while (true) {
367
+ int len = is.read(buffer);
368
+ if (len < 0) {
369
+ break;
370
+ }
371
+ bo.write(buffer, 0, len);
372
+ }
373
+ return bo.toByteArray();
374
+ }
5
375
  }
@@ -0,0 +1,6 @@
1
+ id,account,time,purchase,comment,json_column
2
+ 1,32864,2015-01-27 19:23:49,20150127,embulk,{"k":true}
3
+ 2,14824,2015-01-27 19:01:23,20150127,embulk jruby,{"k":1}
4
+ 3,27559,2015-01-28 02:20:02,20150128,"Embulk ""csv"" parser plugin",{"k":1.23}
5
+ 4,11270,2015-01-29 11:54:36,20150129,NULL,{"k":"v"}
6
+ 5,53231,2015-01-30 13:48:12,20150130,NULL,{"k":"2015-02-03 08:13:45"}
@@ -0,0 +1,6 @@
1
+ id,account,time,purchase,comment,json_column
2
+ 1,32864,2015-01-27 19:23:49,20150127,embulk,{"k":true}
3
+ 2,14824,2015-01-27 19:01:23,20150127,embulk jruby,{"k":1}
4
+ 3,27559,2015-01-28 02:20:02,20150128,"Embulk ""csv"" parser plugin",{"k":1.23}
5
+ 4,11270,2015-01-29 11:54:36,20150129,NULL,{"k":"v"}
6
+ 5,53231,2015-01-30 13:48:12,20150130,NULL,{"k":"2015-02-03 08:13:45"}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embulk-output-ftp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Satoshi Akama
@@ -59,17 +59,17 @@ files:
59
59
  - gradlew.bat
60
60
  - lib/embulk/output/ftp.rb
61
61
  - libs/ftp4j-1.7.2.jar
62
- - src/main/java/org/embulk/output/ftp/BlockingTransfer.java
63
62
  - src/main/java/org/embulk/output/ftp/FtpFileOutputPlugin.java
64
63
  - src/main/java/org/embulk/output/ftp/SSLPlugins.java
65
64
  - src/main/java/org/embulk/output/ftp/TrustManagers.java
66
- - src/test/java/org/embulk/output/ftp/TestBlockingTransfer.java
67
65
  - src/test/java/org/embulk/output/ftp/TestFtpFileOutputPlugin.java
68
66
  - src/test/java/org/embulk/output/ftp/TestSSLPlugins.java
69
67
  - src/test/java/org/embulk/output/ftp/TrustedManagers.java
68
+ - src/test/resources/sample_01.csv
69
+ - src/test/resources/sample_02.csv
70
70
  - classpath/bcpkix-jdk15on-1.52.jar
71
71
  - classpath/bcprov-jdk15on-1.52.jar
72
- - classpath/embulk-output-ftp-0.1.0.jar
72
+ - classpath/embulk-output-ftp-0.1.1.jar
73
73
  - classpath/ftp4j-1.7.2.jar
74
74
  homepage: https://github.com/sakama/embulk-output-ftp
75
75
  licenses:
Binary file
@@ -1,277 +0,0 @@
1
- package org.embulk.output.ftp;
2
-
3
- import com.google.common.base.Function;
4
-
5
- import java.io.EOFException;
6
- import java.io.IOException;
7
- import java.io.InterruptedIOException;
8
- import java.nio.ByteBuffer;
9
- import java.nio.channels.ReadableByteChannel;
10
- import java.nio.channels.WritableByteChannel;
11
- import java.util.concurrent.Callable;
12
- import java.util.concurrent.CancellationException;
13
- import java.util.concurrent.ExecutionException;
14
- import java.util.concurrent.ExecutorService;
15
- import java.util.concurrent.Future;
16
-
17
- public class BlockingTransfer
18
- {
19
- private final WriterChannel writerChannel;
20
- private final ReaderChannel readerChannel;
21
- private Future<?> transferCompletionFuture;
22
-
23
- public static BlockingTransfer submit(ExecutorService executor,
24
- Function<BlockingTransfer, Runnable> starterFactory)
25
- {
26
- BlockingTransfer transfer = new BlockingTransfer();
27
- final Runnable starter = starterFactory.apply(transfer);
28
- transfer.setTransferCompletionFuture(
29
- executor.submit(new Callable<Void>() {
30
- public Void call() throws Exception
31
- {
32
- starter.run();
33
- return null;
34
- }
35
- })
36
- );
37
- return transfer;
38
- }
39
-
40
- private BlockingTransfer()
41
- {
42
- this.writerChannel = new WriterChannel();
43
- this.readerChannel = new ReaderChannel();
44
- }
45
-
46
- private void setTransferCompletionFuture(Future<?> future)
47
- {
48
- this.transferCompletionFuture = future;
49
- }
50
-
51
- public ReadableByteChannel getReaderChannel()
52
- {
53
- return readerChannel;
54
- }
55
-
56
- public WritableByteChannel getWriterChannel()
57
- {
58
- return writerChannel;
59
- }
60
-
61
- public void transferFailed(Throwable exception)
62
- {
63
- readerChannel.overwriteException(exception);
64
- }
65
-
66
- void waitForTransferCompletion() throws IOException
67
- {
68
- Future<?> f = transferCompletionFuture;
69
- if (f != null) {
70
- try {
71
- f.get();
72
- }
73
- catch (CancellationException | InterruptedException ex) {
74
- throw new InterruptedIOException();
75
- }
76
- catch (ExecutionException ex) {
77
- // transfer failed
78
- Throwable e = ex.getCause();
79
- if (e instanceof IOException) {
80
- throw (IOException) e;
81
- }
82
- else if (e instanceof RuntimeException) {
83
- throw (RuntimeException) e;
84
- }
85
- else if (e instanceof Error) {
86
- throw (Error) e;
87
- }
88
- else {
89
- throw new IOException(e);
90
- }
91
- }
92
- }
93
- }
94
-
95
- public class WriterChannel implements WritableByteChannel
96
- {
97
- public int write(ByteBuffer src) throws IOException
98
- {
99
- int sz = src.remaining();
100
- if (sz <= 0) {
101
- return sz;
102
- }
103
-
104
- synchronized (readerChannel) {
105
- if (!readerChannel.waitForWritable()) {
106
- return -1;
107
- }
108
-
109
- readerChannel.setBuffer(src);
110
-
111
- if (!readerChannel.waitForWritable()) { // wait for complete processing src
112
- return -1;
113
- }
114
- }
115
-
116
- return sz - src.remaining();
117
- }
118
-
119
- public boolean isOpen()
120
- {
121
- return readerChannel.isOpen();
122
- }
123
-
124
- public void close() throws IOException
125
- {
126
- readerChannel.closePeer();
127
- waitForTransferCompletion();
128
- }
129
- }
130
-
131
- private static int transferByteBuffer(ByteBuffer src, ByteBuffer dst)
132
- {
133
- int pos = dst.position();
134
-
135
- int srcrem = src.remaining();
136
- int dstrem = dst.remaining();
137
- if (dstrem < srcrem) {
138
- int lim = src.limit();
139
- try {
140
- src.limit(src.position() + dstrem);
141
- dst.put(src);
142
- }
143
- finally {
144
- src.limit(lim);
145
- }
146
- }
147
- else {
148
- dst.put(src);
149
- }
150
-
151
- return dst.position() - pos;
152
- }
153
-
154
- public class ReaderChannel implements ReadableByteChannel
155
- {
156
- private ByteBuffer buffer;
157
- private Throwable exception;
158
-
159
- public synchronized int read(ByteBuffer dst) throws IOException
160
- {
161
- if (!waitForReadable()) {
162
- return -1;
163
- }
164
-
165
- int len = transferByteBuffer(buffer, dst);
166
- if (!buffer.hasRemaining()) {
167
- setBuffer(null);
168
- notifyAll();
169
- }
170
-
171
- return len;
172
- }
173
-
174
- public synchronized boolean isOpen()
175
- {
176
- return exception == null;
177
- }
178
-
179
- public void close() throws IOException
180
- {
181
- setException(new EOFException("reader closed channel"));
182
- }
183
-
184
- private void setBuffer(ByteBuffer buffer)
185
- {
186
- this.buffer = buffer;
187
- notifyAll();
188
- }
189
-
190
- private synchronized boolean waitForWritable() throws IOException
191
- {
192
- while (buffer != null) {
193
- if (exception != null) {
194
- if (exception instanceof EOFException) {
195
- return false;
196
- }
197
- throwException();
198
- }
199
-
200
- try {
201
- wait();
202
- }
203
- catch (InterruptedException ex) {
204
- // TODO throws ClosedByInterruptException or InterruptedIOException?
205
- }
206
- }
207
-
208
- return true;
209
- }
210
-
211
- private boolean waitForReadable() throws IOException
212
- {
213
- while (buffer == null) {
214
- if (exception != null) {
215
- if (exception instanceof EOFException) {
216
- return false;
217
- }
218
- throwException();
219
- }
220
-
221
- try {
222
- wait();
223
- }
224
- catch (InterruptedException ex) {
225
- // TODO throws ClosedByInterruptException or InterruptedIOException?
226
- }
227
- }
228
-
229
- return true;
230
- }
231
-
232
- public synchronized void closePeer() throws IOException
233
- {
234
- waitForWritable();
235
- if (exception != null && !(exception instanceof EOFException)) {
236
- throwException();
237
- }
238
- setException(new EOFException("writer closed channel"));
239
- }
240
-
241
- public synchronized void setException(Throwable exception)
242
- {
243
- if (this.exception == null) {
244
- this.exception = exception;
245
- }
246
- notifyAll();
247
- }
248
-
249
- public synchronized void overwriteException(Throwable exception)
250
- {
251
- this.exception = exception;
252
- notifyAll();
253
- }
254
-
255
- public boolean hasException()
256
- {
257
- return exception != null;
258
- }
259
-
260
- public void throwException() throws IOException
261
- {
262
- Throwable ex = exception;
263
- if (ex instanceof IOException) {
264
- throw (IOException) ex;
265
- }
266
- else if (ex instanceof RuntimeException) {
267
- throw (RuntimeException) ex;
268
- }
269
- else if (ex instanceof Error) {
270
- throw (Error) ex;
271
- }
272
- else {
273
- throw new IOException(ex);
274
- }
275
- }
276
- }
277
- }
@@ -1,5 +0,0 @@
1
- package org.embulk.output.ftp;
2
-
3
- public class TestBlockingTransfer
4
- {
5
- }