embulk-output-sftp 0.0.9 → 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: 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