embulk-output-sftp 0.0.9 → 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: 3bd5fb09a08a951dc92e4a12745815cde65af4a4
4
- data.tar.gz: ab89f5c32965abac6cc6437211b7f55bc9973d6d
3
+ metadata.gz: 831b167ddd205c7ad62b1306a47c634eb7c52aa5
4
+ data.tar.gz: 2de997335de69c887b58507cd820be9593a06483
5
5
  SHA512:
6
- metadata.gz: 973356f2f106e7244e1e4865d81450146312a92df47952f435e05e982fd4ba6403b1ca78d19c4c0e79253c6080ff00f54cb843e5fb652d00836a745fe32a28f2
7
- data.tar.gz: 8f2323398bd3f9c54eb26bf51b792e82198789e19c27333ade336130e296189bea54a5c970fc79a3820867d25d2407a2a739175c127c80d7341a415b830e4b6c
6
+ metadata.gz: cfa0bf45de009ad868e66ded095e693f53aa3a5db03d166e6460ec686668e09018106868b055bf2fbe7aaa18522beb80701de7a85768910fcbb4bfa6331860e7
7
+ data.tar.gz: e7ea2ee48cc39c457a57dc368ac87918697aa85c1e372fe64a7740cbaccc360ec0ce10814bad6b7029bf51799b610b7d2c441a26a38edf7b8aeb141f35bee1d1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ 0.1.1 (2017-05-29)
2
+ - Fix: Improve retry logic
3
+ - https://github.com/embulk/embulk-output-sftp/pull/34
4
+
1
5
  0.0.9 (2017-03-09)
2
6
  ==================
3
7
  - Fix: Hide password in the log
data/README.md CHANGED
@@ -1,6 +1,5 @@
1
- # Sftp file output plugin for Embulk
2
- [![Build Status](https://travis-ci.org/civitaspo/embulk-output-sftp.svg)](https://travis-ci.org/civitaspo/embulk-output-sftp)
3
- [![Coverage Status](https://coveralls.io/repos/civitaspo/embulk-output-sftp/badge.svg?branch=master&service=github)](https://coveralls.io/github/civitaspo/embulk-output-sftp?branch=master)
1
+ # SFTP file output plugin for Embulk
2
+ [![Build Status](https://travis-ci.org/embulk/embulk-output-sftp.svg)](https://travis-ci.org/embulk/embulk-output-sftp)
4
3
 
5
4
  Stores files on a SFTP Server
6
5
 
@@ -125,6 +124,7 @@ $ embulk run -Ilib example/sample.yml
125
124
 
126
125
  ```
127
126
  $ ./gradlew gem # -t to watch change of files and rebuild continuously
127
+ $ ./gradlew bintrayUpload # release embulk-output-sftp to Bintray maven repo
128
128
  ```
129
129
 
130
130
  ## Note
data/build.gradle CHANGED
@@ -1,5 +1,6 @@
1
1
  plugins {
2
- id "com.jfrog.bintray" version "1.1"
2
+ id "com.jfrog.bintray" version "1.7"
3
+ id "maven-publish"
3
4
  id "com.github.jruby-gradle.base" version "0.1.5"
4
5
  id "com.github.kt3k.coveralls" version "2.4.0"
5
6
  id "jacoco"
@@ -15,7 +16,8 @@ configurations {
15
16
  provided
16
17
  }
17
18
 
18
- version = "0.0.9"
19
+ group = "org.embulk.output.sftp"
20
+ version = "0.1.1"
19
21
  sourceCompatibility = 1.7
20
22
  targetCompatibility = 1.7
21
23
 
@@ -24,6 +26,7 @@ dependencies {
24
26
  provided "org.embulk:embulk-core:0.8.6"
25
27
  // compile "YOUR_JAR_DEPENDENCY_GROUP:YOUR_JAR_DEPENDENCY_MODULE:YOUR_JAR_DEPENDENCY_VERSION"
26
28
  compile "org.apache.commons:commons-vfs2:2.1.1660580.2"
29
+ compile "commons-io:commons-io:2.5"
27
30
  compile "com.jcraft:jsch:0.1.53"
28
31
  testCompile "junit:junit:4.+"
29
32
  testCompile "org.embulk:embulk-core:0.8.6:tests"
@@ -40,6 +43,50 @@ jacocoTestReport {
40
43
  }
41
44
  }
42
45
 
46
+ javadoc {
47
+ options {
48
+ locale = 'en_US'
49
+ encoding = 'UTF-8'
50
+ }
51
+ }
52
+
53
+ // bintray
54
+ bintray {
55
+ // write at your bintray user name and api key to ~/.gradle/gradle.properties file:
56
+ user = project.hasProperty('bintray_user') ? bintray_user : ''
57
+ key = project.hasProperty('bintray_api_key') ? bintray_api_key : ''
58
+
59
+ publications = ['bintrayMavenRelease']
60
+ publish = true
61
+
62
+ pkg {
63
+ userOrg = 'embulk-output-sftp'
64
+ repo = 'maven'
65
+ name = project.name
66
+ desc = 'SFTP output plugin for Embulk'
67
+ websiteUrl = 'https://github.com/embulk/embulk-output-sftp'
68
+ issueTrackerUrl = 'https://github.com/embulk/embulk-output-sftp/issues'
69
+ vcsUrl = 'https://github.com/embulk/embulk-output-sftp.git'
70
+ licenses = ['MIT']
71
+ labels = ['embulk', 'java']
72
+ publicDownloadNumbers = true
73
+
74
+ version {
75
+ name = project.version
76
+ }
77
+ }
78
+ }
79
+ publishing {
80
+ publications {
81
+ bintrayMavenRelease(MavenPublication) {
82
+ from components.java
83
+ artifact testsJar
84
+ artifact sourcesJar
85
+ artifact javadocJar
86
+ }
87
+ }
88
+ }
89
+
43
90
  task classpath(type: Copy, dependsOn: ["jar"]) {
44
91
  doFirst { file("classpath").deleteDir() }
45
92
  from (configurations.runtime - configurations.provided + files(jar.archivePath))
@@ -47,6 +94,20 @@ task classpath(type: Copy, dependsOn: ["jar"]) {
47
94
  }
48
95
  clean { delete "classpath" }
49
96
 
97
+ // add tests/javadoc/source jar tasks as artifacts to be released
98
+ task testsJar(type: Jar, dependsOn: classes) {
99
+ classifier = 'tests'
100
+ from sourceSets.test.output
101
+ }
102
+ task sourcesJar(type: Jar, dependsOn: classes) {
103
+ classifier = 'sources'
104
+ from sourceSets.main.allSource
105
+ }
106
+ task javadocJar(type: Jar, dependsOn: javadoc) {
107
+ classifier = 'javadoc'
108
+ from javadoc.destinationDir
109
+ }
110
+
50
111
  checkstyle {
51
112
  configFile = file("${project.rootDir}/config/checkstyle/checkstyle.xml")
52
113
  toolVersion = '6.14.1'
@@ -88,12 +149,12 @@ task gemspec {
88
149
  Gem::Specification.new do |spec|
89
150
  spec.name = "${project.name}"
90
151
  spec.version = "${project.version}"
91
- spec.authors = ["Civitaspo"]
92
- spec.summary = %[Sftp file output plugin for Embulk]
93
- spec.description = %[Stores files on Sftp.]
94
- spec.email = ["civitaspo@gmail.com"]
152
+ spec.authors = ["Civitaspo", "Satoshi Akama"]
153
+ spec.summary = %[SFTP file output plugin for Embulk]
154
+ spec.description = %[Stores files on SFTP server.]
155
+ spec.email = ["civitaspo@gmail.com", "satoshiakama@gmail.com"]
95
156
  spec.licenses = ["MIT"]
96
- spec.homepage = "https://github.com/civitaspo/embulk-output-sftp"
157
+ spec.homepage = "https://github.com/embulk/embulk-output-sftp"
97
158
 
98
159
  spec.files = `git ls-files`.split("\n") + Dir["classpath/*.jar"]
99
160
  spec.test_files = spec.files.grep(%r"^(test|spec)/")
Binary file
@@ -2,6 +2,7 @@ package org.embulk.output.sftp;
2
2
 
3
3
  import com.google.common.base.Function;
4
4
  import com.google.common.base.Throwables;
5
+ import org.apache.commons.io.IOUtils;
5
6
  import org.apache.commons.vfs2.FileObject;
6
7
  import org.apache.commons.vfs2.FileSystemException;
7
8
  import org.apache.commons.vfs2.FileSystemOptions;
@@ -15,15 +16,22 @@ import org.embulk.spi.Exec;
15
16
  import org.embulk.spi.FileOutput;
16
17
  import org.embulk.spi.TransactionalFileOutput;
17
18
  import org.embulk.spi.unit.LocalFile;
19
+ import org.embulk.spi.util.RetryExecutor.RetryGiveupException;
20
+ import org.embulk.spi.util.RetryExecutor.Retryable;
18
21
  import org.slf4j.Logger;
19
22
 
23
+ import java.io.BufferedInputStream;
24
+ import java.io.BufferedOutputStream;
20
25
  import java.io.File;
26
+ import java.io.FileInputStream;
27
+ import java.io.FileNotFoundException;
28
+ import java.io.FileOutputStream;
21
29
  import java.io.IOException;
22
- import java.io.OutputStream;
23
30
  import java.net.URI;
24
31
  import java.net.URISyntaxException;
25
32
 
26
33
  import static org.embulk.output.sftp.SftpFileOutputPlugin.PluginTask;
34
+ import static org.embulk.spi.util.RetryExecutor.retryExecutor;
27
35
 
28
36
  /**
29
37
  * Created by takahiro.nakayama on 10/20/15.
@@ -44,8 +52,8 @@ public class SftpFileOutput
44
52
 
45
53
  private final int taskIndex;
46
54
  private int fileIndex = 0;
47
- private FileObject currentFile;
48
- private OutputStream currentFileOutputStream;
55
+ private File tempFile;
56
+ private BufferedOutputStream localOutput = null;
49
57
 
50
58
  private StandardFileSystemManager initializeStandardFileSystemManager()
51
59
  {
@@ -144,11 +152,10 @@ public class SftpFileOutput
144
152
  closeCurrentFile();
145
153
 
146
154
  try {
147
- currentFile = newSftpFile(getSftpFileUri(getOutputFilePath()));
148
- currentFileOutputStream = newSftpOutputStream(currentFile);
149
- logger.info("new sftp file: {}", currentFile.getPublicURIString());
155
+ tempFile = Exec.getTempFileSpace().createTempFile();
156
+ localOutput = new BufferedOutputStream(new FileOutputStream(tempFile));
150
157
  }
151
- catch (FileSystemException e) {
158
+ catch (FileNotFoundException e) {
152
159
  logger.error(e.getMessage());
153
160
  Throwables.propagate(e);
154
161
  }
@@ -157,28 +164,11 @@ public class SftpFileOutput
157
164
  @Override
158
165
  public void add(final Buffer buffer)
159
166
  {
160
- if (currentFile == null) {
161
- throw new IllegalStateException("nextFile() must be called before poll()");
162
- }
163
-
164
167
  try {
165
- Retriable<Void> retriable = new Retriable<Void>() {
166
- public Void execute() throws IOException
167
- {
168
- currentFileOutputStream.write(buffer.array(), buffer.offset(), buffer.limit());
169
- return null;
170
- }
171
- };
172
- try {
173
- withConnectionRetry(retriable);
174
- }
175
- catch (Exception e) {
176
- throw (IOException) e;
177
- }
168
+ localOutput.write(buffer.array(), buffer.offset(), buffer.limit());
178
169
  }
179
- catch (IOException e) {
180
- logger.error(e.getMessage());
181
- Throwables.propagate(e);
170
+ catch (IOException ex) {
171
+ throw Throwables.propagate(ex);
182
172
  }
183
173
  finally {
184
174
  buffer.release();
@@ -189,6 +179,7 @@ public class SftpFileOutput
189
179
  public void finish()
190
180
  {
191
181
  closeCurrentFile();
182
+ uploadFile(getOutputFilePath());
192
183
  }
193
184
 
194
185
  @Override
@@ -211,27 +202,68 @@ public class SftpFileOutput
211
202
 
212
203
  private void closeCurrentFile()
213
204
  {
214
- if (currentFile == null) {
215
- return;
205
+ if (localOutput != null) {
206
+ try {
207
+ localOutput.close();
208
+ }
209
+ catch (IOException ex) {
210
+ throw Throwables.propagate(ex);
211
+ }
216
212
  }
213
+ }
217
214
 
215
+ private Void uploadFile(final String remotePath)
216
+ {
218
217
  try {
219
- currentFileOutputStream.close();
220
- }
221
- catch (IOException e) {
222
- logger.info(e.getMessage());
218
+ return retryExecutor()
219
+ .withRetryLimit(maxConnectionRetry)
220
+ .withInitialRetryWait(500)
221
+ .withMaxRetryWait(30 * 1000)
222
+ .runInterruptible(new Retryable<Void>() {
223
+ @Override
224
+ public Void call() throws IOException
225
+ {
226
+ FileObject remoteFile = newSftpFile(getSftpFileUri(remotePath));
227
+ logger.info("new sftp file: {}", remoteFile.getPublicURIString());
228
+ try (BufferedOutputStream outputStream = new BufferedOutputStream(remoteFile.getContent().getOutputStream())) {
229
+ try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(tempFile))) {
230
+ IOUtils.copy(inputStream, outputStream);
231
+ }
232
+ }
233
+ return null;
234
+ }
235
+
236
+ @Override
237
+ public boolean isRetryableException(Exception exception)
238
+ {
239
+ return true;
240
+ }
241
+
242
+ @Override
243
+ public void onRetry(Exception exception, int retryCount, int retryLimit, int retryWait) throws RetryGiveupException
244
+ {
245
+ String message = String.format("SFTP output failed. Retrying %d/%d after %d seconds. Message: %s",
246
+ retryCount, retryLimit, retryWait / 1000, exception.getMessage());
247
+ if (retryCount % 3 == 0) {
248
+ logger.warn(message, exception);
249
+ }
250
+ else {
251
+ logger.warn(message);
252
+ }
253
+ }
254
+
255
+ @Override
256
+ public void onGiveup(Exception firstException, Exception lastException) throws RetryGiveupException
257
+ {
258
+ }
259
+ });
223
260
  }
224
-
225
- try {
226
- currentFile.close();
261
+ catch (RetryGiveupException ex) {
262
+ throw Throwables.propagate(ex.getCause());
227
263
  }
228
- catch (FileSystemException e) {
229
- logger.warn(e.getMessage());
264
+ catch (InterruptedException ex) {
265
+ throw Throwables.propagate(ex);
230
266
  }
231
-
232
- fileIndex++;
233
- currentFile = null;
234
- currentFileOutputStream = null;
235
267
  }
236
268
 
237
269
  private URI getSftpFileUri(String remoteFilePath)
@@ -250,84 +282,20 @@ public class SftpFileOutput
250
282
  return pathPrefix + String.format(sequenceFormat, taskIndex, fileIndex) + fileNameExtension;
251
283
  }
252
284
 
253
- interface Retriable<T>
254
- {
255
- /**
256
- * Execute the operation with the given (or null) return value.
257
- * @return any return value from the operation
258
- * @throws Exception
259
- */
260
- public T execute() throws Exception;
261
- }
262
-
263
- private <T> T withConnectionRetry(final Retriable<T> op)
264
- throws Exception
285
+ private FileObject newSftpFile(final URI sftpUri) throws FileSystemException
265
286
  {
266
- int count = 0;
267
- while (true) {
268
- try {
269
- return op.execute();
270
- }
271
- catch (final Exception e) {
272
- if (++count > maxConnectionRetry) {
273
- throw e;
274
- }
275
- logger.warn("failed to connect sftp server: " + e.getMessage(), e);
276
-
277
- try {
278
- long sleepTime = ((long) Math.pow(2, count) * 1000);
279
- logger.warn("sleep in next connection retry: {} milliseconds", sleepTime);
280
- Thread.sleep(sleepTime); // milliseconds
281
- }
282
- catch (InterruptedException e1) {
283
- // Ignore this exception because this exception is just about `sleep`.
284
- logger.warn(e1.getMessage(), e1);
285
- }
286
- logger.warn("retry to connect sftp server: " + count + " times");
287
- }
287
+ FileObject file = manager.resolveFile(sftpUri.toString(), fsOptions);
288
+ if (file.exists()) {
289
+ file.delete();
288
290
  }
289
- }
290
-
291
- private FileObject newSftpFile(final URI sftpUri)
292
- throws FileSystemException
293
- {
294
- Retriable<FileObject> retriable = new Retriable<FileObject>() {
295
- public FileObject execute() throws FileSystemException
296
- {
297
- FileObject file = manager.resolveFile(sftpUri.toString(), fsOptions);
298
- if (file.getParent().exists()) {
299
- logger.info("parent directory {} exists there", file.getParent().getPublicURIString());
300
- }
301
- else {
302
- logger.info("trying to create parent directory {}", file.getParent().getPublicURIString());
303
- file.getParent().createFolder();
304
- }
305
- return file;
306
- }
307
- };
308
- try {
309
- return withConnectionRetry(retriable);
310
- }
311
- catch (Exception e) {
312
- throw (FileSystemException) e;
313
- }
314
- }
315
-
316
- private OutputStream newSftpOutputStream(final FileObject file)
317
- throws FileSystemException
318
- {
319
- Retriable<OutputStream> retriable = new Retriable<OutputStream>() {
320
- public OutputStream execute() throws FileSystemException
321
- {
322
- return file.getContent().getOutputStream();
323
- }
324
- };
325
- try {
326
- return withConnectionRetry(retriable);
291
+ if (file.getParent().exists()) {
292
+ logger.info("parent directory {} exists there", file.getParent().getPublicURIString());
327
293
  }
328
- catch (Exception e) {
329
- throw (FileSystemException) e;
294
+ else {
295
+ logger.info("trying to create parent directory {}", file.getParent().getPublicURIString());
296
+ file.getParent().createFolder();
330
297
  }
298
+ return file;
331
299
  }
332
300
 
333
301
  private Function<LocalFile, String> localFileToPathString()
metadata CHANGED
@@ -1,46 +1,48 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embulk-output-sftp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Civitaspo
8
+ - Satoshi Akama
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2017-03-09 00:00:00.000000000 Z
12
+ date: 2017-05-29 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
- requirement: !ruby/object:Gem::Requirement
15
+ name: bundler
16
+ version_requirements: !ruby/object:Gem::Requirement
15
17
  requirements:
16
18
  - - ~>
17
19
  - !ruby/object:Gem::Version
18
20
  version: '1.0'
19
- name: bundler
20
- prerelease: false
21
- type: :development
22
- version_requirements: !ruby/object:Gem::Requirement
21
+ requirement: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - ~>
25
24
  - !ruby/object:Gem::Version
26
25
  version: '1.0'
26
+ prerelease: false
27
+ type: :development
27
28
  - !ruby/object:Gem::Dependency
28
- requirement: !ruby/object:Gem::Requirement
29
+ name: rake
30
+ version_requirements: !ruby/object:Gem::Requirement
29
31
  requirements:
30
32
  - - '>='
31
33
  - !ruby/object:Gem::Version
32
34
  version: '10.0'
33
- name: rake
34
- prerelease: false
35
- type: :development
36
- version_requirements: !ruby/object:Gem::Requirement
35
+ requirement: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - '>='
39
38
  - !ruby/object:Gem::Version
40
39
  version: '10.0'
41
- description: Stores files on Sftp.
40
+ prerelease: false
41
+ type: :development
42
+ description: Stores files on SFTP server.
42
43
  email:
43
44
  - civitaspo@gmail.com
45
+ - satoshiakama@gmail.com
44
46
  executables: []
45
47
  extensions: []
46
48
  extra_rdoc_files: []
@@ -67,11 +69,12 @@ files:
67
69
  - src/test/java/org/embulk/output/sftp/TestSftpFileOutputPlugin.java
68
70
  - src/test/resources/id_rsa
69
71
  - src/test/resources/id_rsa.pub
72
+ - classpath/commons-io-2.5.jar
70
73
  - classpath/commons-logging-1.2.jar
71
74
  - classpath/commons-vfs2-2.1.1660580.2.jar
72
- - classpath/embulk-output-sftp-0.0.9.jar
75
+ - classpath/embulk-output-sftp-0.1.1.jar
73
76
  - classpath/jsch-0.1.53.jar
74
- homepage: https://github.com/civitaspo/embulk-output-sftp
77
+ homepage: https://github.com/embulk/embulk-output-sftp
75
78
  licenses:
76
79
  - MIT
77
80
  metadata: {}
@@ -94,5 +97,5 @@ rubyforge_project:
94
97
  rubygems_version: 2.1.9
95
98
  signing_key:
96
99
  specification_version: 4
97
- summary: Sftp file output plugin for Embulk
100
+ summary: SFTP file output plugin for Embulk
98
101
  test_files: []
Binary file