embulk-output-s3 1.2.0 → 1.3.0

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.
@@ -1,108 +1,108 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE module PUBLIC
3
- "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
4
- "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
5
- <!--
6
- This is a subset of ./checkstyle.xml which allows some loose styles
7
- -->
8
- <module name="Checker">
9
- <module name="FileTabCharacter"/>
10
- <module name="NewlineAtEndOfFile">
11
- <property name="lineSeparator" value="lf"/>
12
- </module>
13
- <module name="RegexpMultiline">
14
- <property name="format" value="\r"/>
15
- <property name="message" value="Line contains carriage return"/>
16
- </module>
17
- <module name="RegexpMultiline">
18
- <property name="format" value=" \n"/>
19
- <property name="message" value="Line has trailing whitespace"/>
20
- </module>
21
- <module name="RegexpMultiline">
22
- <property name="format" value="\n\n\n"/>
23
- <property name="message" value="Multiple consecutive blank lines"/>
24
- </module>
25
- <module name="RegexpMultiline">
26
- <property name="format" value="\n\n\Z"/>
27
- <property name="message" value="Blank line before end of file"/>
28
- </module>
29
-
30
- <module name="TreeWalker">
31
- <module name="EmptyBlock">
32
- <property name="option" value="text"/>
33
- <property name="tokens" value="
34
- LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_IF,
35
- LITERAL_FOR, LITERAL_TRY, LITERAL_WHILE, INSTANCE_INIT, STATIC_INIT"/>
36
- </module>
37
- <module name="EmptyStatement"/>
38
- <module name="EmptyForInitializerPad"/>
39
- <module name="EmptyForIteratorPad">
40
- <property name="option" value="space"/>
41
- </module>
42
- <module name="MethodParamPad">
43
- <property name="allowLineBreaks" value="true"/>
44
- <property name="option" value="nospace"/>
45
- </module>
46
- <module name="ParenPad"/>
47
- <module name="TypecastParenPad"/>
48
- <module name="NeedBraces"/>
49
- <module name="LeftCurly">
50
- <property name="option" value="nl"/>
51
- <property name="tokens" value="CLASS_DEF, CTOR_DEF, INTERFACE_DEF, METHOD_DEF"/>
52
- </module>
53
- <module name="LeftCurly">
54
- <property name="option" value="eol"/>
55
- <property name="tokens" value="
56
- LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR,
57
- LITERAL_IF, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE"/>
58
- </module>
59
- <module name="RightCurly">
60
- <property name="option" value="alone"/>
61
- </module>
62
- <module name="GenericWhitespace"/>
63
- <module name="WhitespaceAfter"/>
64
- <module name="NoWhitespaceBefore"/>
65
-
66
- <module name="UpperEll"/>
67
- <module name="DefaultComesLast"/>
68
- <module name="ArrayTypeStyle"/>
69
- <module name="MultipleVariableDeclarations"/>
70
- <module name="ModifierOrder"/>
71
- <module name="OneStatementPerLine"/>
72
- <module name="StringLiteralEquality"/>
73
- <module name="MutableException"/>
74
- <module name="EqualsHashCode"/>
75
- <module name="InnerAssignment"/>
76
- <module name="InterfaceIsType"/>
77
- <module name="HideUtilityClassConstructor"/>
78
-
79
- <module name="MemberName"/>
80
- <module name="LocalVariableName"/>
81
- <module name="LocalFinalVariableName"/>
82
- <module name="TypeName"/>
83
- <module name="PackageName"/>
84
- <module name="ParameterName"/>
85
- <module name="StaticVariableName"/>
86
- <module name="ClassTypeParameterName">
87
- <property name="format" value="^[A-Z][0-9]?$"/>
88
- </module>
89
- <module name="MethodTypeParameterName">
90
- <property name="format" value="^[A-Z][0-9]?$"/>
91
- </module>
92
-
93
- <module name="WhitespaceAround">
94
- <property name="allowEmptyConstructors" value="true"/>
95
- <property name="allowEmptyMethods" value="true"/>
96
- <property name="ignoreEnhancedForColon" value="false"/>
97
- <property name="tokens" value="
98
- ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN,
99
- BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, EQUAL, GE, GT, LAND, LE,
100
- LITERAL_ASSERT, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
101
- LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
102
- LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE,
103
- LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL,
104
- PLUS, PLUS_ASSIGN, QUESTION, SL, SLIST, SL_ASSIGN, SR, SR_ASSIGN,
105
- STAR, STAR_ASSIGN, TYPE_EXTENSION_AND"/>
106
- </module>
107
- </module>
108
- </module>
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE module PUBLIC
3
+ "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
4
+ "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
5
+ <!--
6
+ This is a subset of ./checkstyle.xml which allows some loose styles
7
+ -->
8
+ <module name="Checker">
9
+ <module name="FileTabCharacter"/>
10
+ <module name="NewlineAtEndOfFile">
11
+ <property name="lineSeparator" value="lf"/>
12
+ </module>
13
+ <module name="RegexpMultiline">
14
+ <property name="format" value="\r"/>
15
+ <property name="message" value="Line contains carriage return"/>
16
+ </module>
17
+ <module name="RegexpMultiline">
18
+ <property name="format" value=" \n"/>
19
+ <property name="message" value="Line has trailing whitespace"/>
20
+ </module>
21
+ <module name="RegexpMultiline">
22
+ <property name="format" value="\n\n\n"/>
23
+ <property name="message" value="Multiple consecutive blank lines"/>
24
+ </module>
25
+ <module name="RegexpMultiline">
26
+ <property name="format" value="\n\n\Z"/>
27
+ <property name="message" value="Blank line before end of file"/>
28
+ </module>
29
+
30
+ <module name="TreeWalker">
31
+ <module name="EmptyBlock">
32
+ <property name="option" value="text"/>
33
+ <property name="tokens" value="
34
+ LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_IF,
35
+ LITERAL_FOR, LITERAL_TRY, LITERAL_WHILE, INSTANCE_INIT, STATIC_INIT"/>
36
+ </module>
37
+ <module name="EmptyStatement"/>
38
+ <module name="EmptyForInitializerPad"/>
39
+ <module name="EmptyForIteratorPad">
40
+ <property name="option" value="space"/>
41
+ </module>
42
+ <module name="MethodParamPad">
43
+ <property name="allowLineBreaks" value="true"/>
44
+ <property name="option" value="nospace"/>
45
+ </module>
46
+ <module name="ParenPad"/>
47
+ <module name="TypecastParenPad"/>
48
+ <module name="NeedBraces"/>
49
+ <module name="LeftCurly">
50
+ <property name="option" value="nl"/>
51
+ <property name="tokens" value="CLASS_DEF, CTOR_DEF, INTERFACE_DEF, METHOD_DEF"/>
52
+ </module>
53
+ <module name="LeftCurly">
54
+ <property name="option" value="eol"/>
55
+ <property name="tokens" value="
56
+ LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR,
57
+ LITERAL_IF, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE"/>
58
+ </module>
59
+ <module name="RightCurly">
60
+ <property name="option" value="alone"/>
61
+ </module>
62
+ <module name="GenericWhitespace"/>
63
+ <module name="WhitespaceAfter"/>
64
+ <module name="NoWhitespaceBefore"/>
65
+
66
+ <module name="UpperEll"/>
67
+ <module name="DefaultComesLast"/>
68
+ <module name="ArrayTypeStyle"/>
69
+ <module name="MultipleVariableDeclarations"/>
70
+ <module name="ModifierOrder"/>
71
+ <module name="OneStatementPerLine"/>
72
+ <module name="StringLiteralEquality"/>
73
+ <module name="MutableException"/>
74
+ <module name="EqualsHashCode"/>
75
+ <module name="InnerAssignment"/>
76
+ <module name="InterfaceIsType"/>
77
+ <module name="HideUtilityClassConstructor"/>
78
+
79
+ <module name="MemberName"/>
80
+ <module name="LocalVariableName"/>
81
+ <module name="LocalFinalVariableName"/>
82
+ <module name="TypeName"/>
83
+ <module name="PackageName"/>
84
+ <module name="ParameterName"/>
85
+ <module name="StaticVariableName"/>
86
+ <module name="ClassTypeParameterName">
87
+ <property name="format" value="^[A-Z][0-9]?$"/>
88
+ </module>
89
+ <module name="MethodTypeParameterName">
90
+ <property name="format" value="^[A-Z][0-9]?$"/>
91
+ </module>
92
+
93
+ <module name="WhitespaceAround">
94
+ <property name="allowEmptyConstructors" value="true"/>
95
+ <property name="allowEmptyMethods" value="true"/>
96
+ <property name="ignoreEnhancedForColon" value="false"/>
97
+ <property name="tokens" value="
98
+ ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN,
99
+ BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, EQUAL, GE, GT, LAND, LE,
100
+ LITERAL_ASSERT, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
101
+ LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
102
+ LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE,
103
+ LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL,
104
+ PLUS, PLUS_ASSIGN, QUESTION, SL, SLIST, SL_ASSIGN, SR, SR_ASSIGN,
105
+ STAR, STAR_ASSIGN, TYPE_EXTENSION_AND"/>
106
+ </module>
107
+ </module>
108
+ </module>
File without changes
@@ -1,6 +1,6 @@
1
- #Wed Jan 13 12:41:02 JST 2016
2
- distributionBase=GRADLE_USER_HOME
3
- distributionPath=wrapper/dists
4
- zipStoreBase=GRADLE_USER_HOME
5
- zipStorePath=wrapper/dists
6
- distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip
1
+ #Wed Jan 13 12:41:02 JST 2016
2
+ distributionBase=GRADLE_USER_HOME
3
+ distributionPath=wrapper/dists
4
+ zipStoreBase=GRADLE_USER_HOME
5
+ zipStorePath=wrapper/dists
6
+ distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip
data/gradlew CHANGED
File without changes
File without changes
@@ -1 +1 @@
1
- rootProject.name = 'embulk-output-s3'
1
+ rootProject.name = 'embulk-output-s3'
@@ -1,301 +1,298 @@
1
- package org.embulk.output;
2
-
3
- import com.amazonaws.ClientConfiguration;
4
- import com.amazonaws.auth.BasicAWSCredentials;
5
- import com.amazonaws.auth.EnvironmentVariableCredentialsProvider;
6
- import com.amazonaws.services.s3.AmazonS3Client;
7
- import com.amazonaws.services.s3.model.CannedAccessControlList;
8
- import com.amazonaws.services.s3.model.PutObjectRequest;
9
- import com.google.common.base.Optional;
10
- import org.embulk.config.Config;
11
- import org.embulk.config.ConfigDefault;
12
- import org.embulk.config.ConfigDiff;
13
- import org.embulk.config.ConfigException;
14
- import org.embulk.config.ConfigSource;
15
- import org.embulk.config.Task;
16
- import org.embulk.config.TaskReport;
17
- import org.embulk.config.TaskSource;
18
- import org.embulk.spi.Buffer;
19
- import org.embulk.spi.Exec;
20
- import org.embulk.spi.FileOutput;
21
- import org.embulk.spi.FileOutputPlugin;
22
- import org.embulk.spi.TransactionalFileOutput;
23
- import org.slf4j.Logger;
24
-
25
- import java.io.IOException;
26
- import java.io.OutputStream;
27
- import java.nio.file.Files;
28
- import java.nio.file.Path;
29
- import java.util.IllegalFormatException;
30
- import java.util.List;
31
- import java.util.Locale;
32
-
33
- public class S3FileOutputPlugin
34
- implements FileOutputPlugin
35
- {
36
- public interface PluginTask
37
- extends Task
38
- {
39
- @Config("path_prefix")
40
- String getPathPrefix();
41
-
42
- @Config("file_ext")
43
- String getFileNameExtension();
44
-
45
- @Config("sequence_format")
46
- @ConfigDefault("\".%03d.%02d\"")
47
- String getSequenceFormat();
48
-
49
- @Config("bucket")
50
- String getBucket();
51
-
52
- @Config("endpoint")
53
- @ConfigDefault("null")
54
- Optional<String> getEndpoint();
55
-
56
- @Config("access_key_id")
57
- @ConfigDefault("null")
58
- Optional<String> getAccessKeyId();
59
-
60
- @Config("secret_access_key")
61
- @ConfigDefault("null")
62
- Optional<String> getSecretAccessKey();
63
-
64
- @Config("tmp_path_prefix")
65
- @ConfigDefault("\"embulk-output-s3-\"")
66
- String getTempPathPrefix();
67
-
68
- @Config("canned_acl")
69
- @ConfigDefault("null")
70
- Optional<CannedAccessControlList> getCannedAccessControlList();
71
- }
72
-
73
- public static class S3FileOutput
74
- implements FileOutput,
75
- TransactionalFileOutput
76
- {
77
- private final Logger log = Exec.getLogger(S3FileOutputPlugin.class);
78
-
79
- private final String bucket;
80
- private final String pathPrefix;
81
- private final String sequenceFormat;
82
- private final String fileNameExtension;
83
- private final String tempPathPrefix;
84
- private final Optional<CannedAccessControlList> cannedAccessControlListOptional;
85
-
86
- private int taskIndex;
87
- private int fileIndex;
88
- private AmazonS3Client client;
89
- private OutputStream current;
90
- private Path tempFilePath;
91
-
92
- private static AmazonS3Client newS3Client(PluginTask task)
93
- {
94
- AmazonS3Client client;
95
-
96
- if (task.getAccessKeyId().isPresent()) {
97
- BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(
98
- task.getAccessKeyId().get(), task.getSecretAccessKey().get());
99
-
100
- ClientConfiguration config = new ClientConfiguration();
101
- // TODO: Support more configurations.
102
-
103
- client = new AmazonS3Client(basicAWSCredentials, config);
104
- }
105
- else {
106
- if (System.getenv("AWS_ACCESS_KEY_ID") == null) {
107
- client = new AmazonS3Client(new EnvironmentVariableCredentialsProvider());
108
- }
109
- else { // IAM ROLE
110
- client = new AmazonS3Client();
111
- }
112
- }
113
- if (task.getEndpoint().isPresent()) {
114
- client.setEndpoint(task.getEndpoint().get());
115
- }
116
-
117
- return client;
118
- }
119
-
120
- public S3FileOutput(PluginTask task, int taskIndex)
121
- {
122
- this.taskIndex = taskIndex;
123
- this.client = newS3Client(task);
124
- this.bucket = task.getBucket();
125
- this.pathPrefix = task.getPathPrefix();
126
- this.sequenceFormat = task.getSequenceFormat();
127
- this.fileNameExtension = task.getFileNameExtension();
128
- this.tempPathPrefix = task.getTempPathPrefix();
129
- this.cannedAccessControlListOptional = task.getCannedAccessControlList();
130
- }
131
-
132
- private static Path newTempFile(String prefix)
133
- throws IOException
134
- {
135
- return Files.createTempFile(prefix, null);
136
- }
137
-
138
- private void deleteTempFile()
139
- {
140
- if (tempFilePath == null) {
141
- return;
142
- }
143
-
144
- try {
145
- Files.delete(tempFilePath);
146
- tempFilePath = null;
147
- }
148
- catch (IOException e) {
149
- throw new RuntimeException(e);
150
- }
151
- }
152
-
153
- private String buildCurrentKey()
154
- {
155
- String sequence = String.format(sequenceFormat, taskIndex,
156
- fileIndex);
157
- return pathPrefix + sequence + fileNameExtension;
158
- }
159
-
160
- private void putFile(Path from, String key)
161
- {
162
- PutObjectRequest request = new PutObjectRequest(bucket, key, from.toFile());
163
- if (cannedAccessControlListOptional.isPresent()) {
164
- request.withCannedAcl(cannedAccessControlListOptional.get());
165
- }
166
- client.putObject(request);
167
- }
168
-
169
- private void closeCurrent()
170
- {
171
- if (current == null) {
172
- return;
173
- }
174
-
175
- try {
176
- putFile(tempFilePath, buildCurrentKey());
177
- fileIndex++;
178
- }
179
- finally {
180
- try {
181
- current.close();
182
- current = null;
183
- }
184
- catch (IOException e) {
185
- throw new RuntimeException(e);
186
- }
187
- finally {
188
- deleteTempFile();
189
- }
190
- }
191
- }
192
-
193
- @Override
194
- public void nextFile()
195
- {
196
- closeCurrent();
197
-
198
- try {
199
- tempFilePath = newTempFile(tempPathPrefix);
200
-
201
- log.info("Writing S3 file '{}'", buildCurrentKey());
202
-
203
- current = Files.newOutputStream(tempFilePath);
204
- }
205
- catch (IOException e) {
206
- throw new RuntimeException(e);
207
- }
208
- }
209
-
210
- @Override
211
- public void add(Buffer buffer)
212
- {
213
- if (current == null) {
214
- throw new IllegalStateException(
215
- "nextFile() must be called before poll()");
216
- }
217
-
218
- try {
219
- current.write(buffer.array(), buffer.offset(), buffer.limit());
220
- }
221
- catch (IOException ex) {
222
- throw new RuntimeException(ex);
223
- }
224
- finally {
225
- buffer.release();
226
- }
227
- }
228
-
229
- @Override
230
- public void finish()
231
- {
232
- closeCurrent();
233
- }
234
-
235
- @Override
236
- public void close()
237
- {
238
- closeCurrent();
239
- }
240
-
241
- @Override
242
- public void abort()
243
- {
244
- deleteTempFile();
245
- }
246
-
247
- @Override
248
- public TaskReport commit()
249
- {
250
- TaskReport report = Exec.newTaskReport();
251
- return report;
252
- }
253
- }
254
-
255
- private void validateSequenceFormat(PluginTask task)
256
- {
257
- try {
258
- @SuppressWarnings("unused")
259
- String dontCare = String.format(Locale.ENGLISH,
260
- task.getSequenceFormat(), 0, 0);
261
- }
262
- catch (IllegalFormatException ex) {
263
- throw new ConfigException(
264
- "Invalid sequence_format: parameter for file output plugin",
265
- ex);
266
- }
267
- }
268
-
269
- @Override
270
- public ConfigDiff transaction(ConfigSource config, int taskCount,
271
- Control control)
272
- {
273
- PluginTask task = config.loadConfig(PluginTask.class);
274
-
275
- validateSequenceFormat(task);
276
-
277
- return resume(task.dump(), taskCount, control);
278
- }
279
-
280
- @Override
281
- public ConfigDiff resume(TaskSource taskSource, int taskCount,
282
- Control control)
283
- {
284
- control.run(taskSource);
285
- return Exec.newConfigDiff();
286
- }
287
-
288
- @Override
289
- public void cleanup(TaskSource taskSource, int taskCount,
290
- List<TaskReport> successTaskReports)
291
- {
292
- }
293
-
294
- @Override
295
- public TransactionalFileOutput open(TaskSource taskSource, int taskIndex)
296
- {
297
- PluginTask task = taskSource.loadTask(PluginTask.class);
298
-
299
- return new S3FileOutput(task, taskIndex);
300
- }
301
- }
1
+ package org.embulk.output;
2
+
3
+ import java.io.IOException;
4
+ import java.io.OutputStream;
5
+ import java.nio.file.Files;
6
+ import java.nio.file.Path;
7
+ import java.util.IllegalFormatException;
8
+ import java.util.List;
9
+ import java.util.Locale;
10
+
11
+ import org.embulk.config.Config;
12
+ import org.embulk.config.ConfigDefault;
13
+ import org.embulk.config.ConfigDiff;
14
+ import org.embulk.config.ConfigException;
15
+ import org.embulk.config.ConfigSource;
16
+ import org.embulk.config.Task;
17
+ import org.embulk.config.TaskReport;
18
+ import org.embulk.config.TaskSource;
19
+ import org.embulk.spi.Buffer;
20
+ import org.embulk.spi.Exec;
21
+ import org.embulk.spi.FileOutput;
22
+ import org.embulk.spi.FileOutputPlugin;
23
+ import org.embulk.spi.TransactionalFileOutput;
24
+ import org.slf4j.Logger;
25
+
26
+ import com.amazonaws.ClientConfiguration;
27
+ import com.amazonaws.auth.BasicAWSCredentials;
28
+ import com.amazonaws.services.s3.AmazonS3Client;
29
+ import com.amazonaws.services.s3.model.CannedAccessControlList;
30
+ import com.amazonaws.services.s3.model.PutObjectRequest;
31
+ import com.google.common.base.Optional;
32
+
33
+ public class S3FileOutputPlugin
34
+ implements FileOutputPlugin
35
+ {
36
+ public interface PluginTask
37
+ extends Task
38
+ {
39
+ @Config("path_prefix")
40
+ String getPathPrefix();
41
+
42
+ @Config("file_ext")
43
+ String getFileNameExtension();
44
+
45
+ @Config("sequence_format")
46
+ @ConfigDefault("\".%03d.%02d\"")
47
+ String getSequenceFormat();
48
+
49
+ @Config("bucket")
50
+ String getBucket();
51
+
52
+ @Config("endpoint")
53
+ @ConfigDefault("null")
54
+ Optional<String> getEndpoint();
55
+
56
+ @Config("access_key_id")
57
+ @ConfigDefault("null")
58
+ Optional<String> getAccessKeyId();
59
+
60
+ @Config("secret_access_key")
61
+ @ConfigDefault("null")
62
+ Optional<String> getSecretAccessKey();
63
+
64
+ @Config("tmp_path_prefix")
65
+ @ConfigDefault("\"embulk-output-s3-\"")
66
+ String getTempPathPrefix();
67
+
68
+ @Config("canned_acl")
69
+ @ConfigDefault("null")
70
+ Optional<CannedAccessControlList> getCannedAccessControlList();
71
+ }
72
+
73
+ public static class S3FileOutput
74
+ implements FileOutput,
75
+ TransactionalFileOutput
76
+ {
77
+ private final Logger log = Exec.getLogger(S3FileOutputPlugin.class);
78
+
79
+ private final String bucket;
80
+ private final String pathPrefix;
81
+ private final String sequenceFormat;
82
+ private final String fileNameExtension;
83
+ private final String tempPathPrefix;
84
+ private final Optional<CannedAccessControlList> cannedAccessControlListOptional;
85
+
86
+ private int taskIndex;
87
+ private int fileIndex;
88
+ private AmazonS3Client client;
89
+ private OutputStream current;
90
+ private Path tempFilePath;
91
+
92
+ private static AmazonS3Client newS3Client(PluginTask task)
93
+ {
94
+ AmazonS3Client client;
95
+
96
+ // TODO: Support more configurations.
97
+ ClientConfiguration config = new ClientConfiguration();
98
+
99
+ if (task.getAccessKeyId().isPresent()) {
100
+ BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(
101
+ task.getAccessKeyId().get(), task.getSecretAccessKey().get());
102
+
103
+ client = new AmazonS3Client(basicAWSCredentials, config);
104
+ }
105
+ else {
106
+ // Use default credential provider chain.
107
+ client = new AmazonS3Client(config);
108
+ }
109
+
110
+ if (task.getEndpoint().isPresent()) {
111
+ client.setEndpoint(task.getEndpoint().get());
112
+ }
113
+
114
+ return client;
115
+ }
116
+
117
+ public S3FileOutput(PluginTask task, int taskIndex)
118
+ {
119
+ this.taskIndex = taskIndex;
120
+ this.client = newS3Client(task);
121
+ this.bucket = task.getBucket();
122
+ this.pathPrefix = task.getPathPrefix();
123
+ this.sequenceFormat = task.getSequenceFormat();
124
+ this.fileNameExtension = task.getFileNameExtension();
125
+ this.tempPathPrefix = task.getTempPathPrefix();
126
+ this.cannedAccessControlListOptional = task.getCannedAccessControlList();
127
+ }
128
+
129
+ private static Path newTempFile(String prefix)
130
+ throws IOException
131
+ {
132
+ return Files.createTempFile(prefix, null);
133
+ }
134
+
135
+ private void deleteTempFile()
136
+ {
137
+ if (tempFilePath == null) {
138
+ return;
139
+ }
140
+
141
+ try {
142
+ Files.delete(tempFilePath);
143
+ tempFilePath = null;
144
+ }
145
+ catch (IOException e) {
146
+ throw new RuntimeException(e);
147
+ }
148
+ }
149
+
150
+ private String buildCurrentKey()
151
+ {
152
+ String sequence = String.format(sequenceFormat, taskIndex,
153
+ fileIndex);
154
+ return pathPrefix + sequence + fileNameExtension;
155
+ }
156
+
157
+ private void putFile(Path from, String key)
158
+ {
159
+ PutObjectRequest request = new PutObjectRequest(bucket, key, from.toFile());
160
+ if (cannedAccessControlListOptional.isPresent()) {
161
+ request.withCannedAcl(cannedAccessControlListOptional.get());
162
+ }
163
+ client.putObject(request);
164
+ }
165
+
166
+ private void closeCurrent()
167
+ {
168
+ if (current == null) {
169
+ return;
170
+ }
171
+
172
+ try {
173
+ putFile(tempFilePath, buildCurrentKey());
174
+ fileIndex++;
175
+ }
176
+ finally {
177
+ try {
178
+ current.close();
179
+ current = null;
180
+ }
181
+ catch (IOException e) {
182
+ throw new RuntimeException(e);
183
+ }
184
+ finally {
185
+ deleteTempFile();
186
+ }
187
+ }
188
+ }
189
+
190
+ @Override
191
+ public void nextFile()
192
+ {
193
+ closeCurrent();
194
+
195
+ try {
196
+ tempFilePath = newTempFile(tempPathPrefix);
197
+
198
+ log.info("Writing S3 file '{}'", buildCurrentKey());
199
+
200
+ current = Files.newOutputStream(tempFilePath);
201
+ }
202
+ catch (IOException e) {
203
+ throw new RuntimeException(e);
204
+ }
205
+ }
206
+
207
+ @Override
208
+ public void add(Buffer buffer)
209
+ {
210
+ if (current == null) {
211
+ throw new IllegalStateException(
212
+ "nextFile() must be called before poll()");
213
+ }
214
+
215
+ try {
216
+ current.write(buffer.array(), buffer.offset(), buffer.limit());
217
+ }
218
+ catch (IOException ex) {
219
+ throw new RuntimeException(ex);
220
+ }
221
+ finally {
222
+ buffer.release();
223
+ }
224
+ }
225
+
226
+ @Override
227
+ public void finish()
228
+ {
229
+ closeCurrent();
230
+ }
231
+
232
+ @Override
233
+ public void close()
234
+ {
235
+ closeCurrent();
236
+ }
237
+
238
+ @Override
239
+ public void abort()
240
+ {
241
+ deleteTempFile();
242
+ }
243
+
244
+ @Override
245
+ public TaskReport commit()
246
+ {
247
+ TaskReport report = Exec.newTaskReport();
248
+ return report;
249
+ }
250
+ }
251
+
252
+ private void validateSequenceFormat(PluginTask task)
253
+ {
254
+ try {
255
+ @SuppressWarnings("unused")
256
+ String dontCare = String.format(Locale.ENGLISH,
257
+ task.getSequenceFormat(), 0, 0);
258
+ }
259
+ catch (IllegalFormatException ex) {
260
+ throw new ConfigException(
261
+ "Invalid sequence_format: parameter for file output plugin",
262
+ ex);
263
+ }
264
+ }
265
+
266
+ @Override
267
+ public ConfigDiff transaction(ConfigSource config, int taskCount,
268
+ Control control)
269
+ {
270
+ PluginTask task = config.loadConfig(PluginTask.class);
271
+
272
+ validateSequenceFormat(task);
273
+
274
+ return resume(task.dump(), taskCount, control);
275
+ }
276
+
277
+ @Override
278
+ public ConfigDiff resume(TaskSource taskSource, int taskCount,
279
+ Control control)
280
+ {
281
+ control.run(taskSource);
282
+ return Exec.newConfigDiff();
283
+ }
284
+
285
+ @Override
286
+ public void cleanup(TaskSource taskSource, int taskCount,
287
+ List<TaskReport> successTaskReports)
288
+ {
289
+ }
290
+
291
+ @Override
292
+ public TransactionalFileOutput open(TaskSource taskSource, int taskIndex)
293
+ {
294
+ PluginTask task = taskSource.loadTask(PluginTask.class);
295
+
296
+ return new S3FileOutput(task, taskIndex);
297
+ }
298
+ }