embulk-output-ftp 0.1.0 → 0.1.1

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