embulk-input-td 0.2.0 → 0.2.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.
@@ -1,7 +1,7 @@
1
1
 
2
2
  Gem::Specification.new do |spec|
3
3
  spec.name = "embulk-input-td"
4
- spec.version = "0.2.0"
4
+ spec.version = "0.2.1"
5
5
  spec.authors = ["Muga Nishizawa"]
6
6
  spec.summary = %[Td input plugin for Embulk]
7
7
  spec.description = %[Loads records from Td.]
@@ -1,11 +1,5 @@
1
1
  package org.embulk.input.td;
2
2
 
3
- import java.io.IOException;
4
- import java.io.InputStream;
5
- import java.util.List;
6
- import java.util.Properties;
7
- import java.util.zip.GZIPInputStream;
8
-
9
3
  import com.fasterxml.jackson.databind.JsonNode;
10
4
  import com.fasterxml.jackson.databind.ObjectMapper;
11
5
  import com.fasterxml.jackson.databind.node.ArrayNode;
@@ -19,17 +13,25 @@ import com.treasuredata.client.TDClientBuilder;
19
13
  import com.treasuredata.client.model.TDJob;
20
14
  import com.treasuredata.client.model.TDJobRequest;
21
15
  import com.treasuredata.client.model.TDJobSummary;
22
- import org.embulk.config.ConfigException;
23
- import org.embulk.config.ConfigInject;
24
- import org.embulk.config.TaskReport;
16
+ import com.treasuredata.client.model.TDResultFormat;
17
+ import java.io.IOException;
18
+ import java.io.InputStream;
19
+ import java.util.List;
20
+ import java.util.Locale;
21
+ import java.util.Properties;
22
+ import java.util.zip.GZIPInputStream;
25
23
  import org.embulk.config.Config;
26
24
  import org.embulk.config.ConfigDefault;
27
25
  import org.embulk.config.ConfigDiff;
26
+ import org.embulk.config.ConfigException;
27
+ import org.embulk.config.ConfigInject;
28
28
  import org.embulk.config.ConfigSource;
29
29
  import org.embulk.config.Task;
30
+ import org.embulk.config.TaskReport;
30
31
  import org.embulk.config.TaskSource;
31
32
  import org.embulk.input.td.writer.BooleanValueWriter;
32
33
  import org.embulk.input.td.writer.DoubleValueWriter;
34
+ import org.embulk.input.td.writer.JsonValueWriter;
33
35
  import org.embulk.input.td.writer.LongValueWriter;
34
36
  import org.embulk.input.td.writer.StringValueWriter;
35
37
  import org.embulk.input.td.writer.ValueWriter;
@@ -42,130 +44,62 @@ import org.embulk.spi.PageBuilder;
42
44
  import org.embulk.spi.PageOutput;
43
45
  import org.embulk.spi.Schema;
44
46
  import org.embulk.spi.type.Type;
47
+ import org.embulk.spi.type.Types;
45
48
  import org.msgpack.core.MessagePack;
46
49
  import org.msgpack.core.MessageUnpacker;
47
50
  import org.msgpack.value.ArrayValue;
48
51
  import org.msgpack.value.Value;
49
52
  import org.slf4j.Logger;
50
53
 
51
- import static com.google.common.base.Optional.fromNullable;
52
- import static com.treasuredata.client.model.TDResultFormat.MESSAGE_PACK_GZ;
53
- import static java.lang.Integer.parseInt;
54
- import static java.util.Locale.ENGLISH;
55
- import static org.embulk.spi.Exec.getLogger;
56
- import static org.embulk.spi.Exec.newConfigDiff;
57
- import static org.embulk.spi.Exec.newTaskReport;
58
- import static org.embulk.spi.type.Types.BOOLEAN;
59
- import static org.embulk.spi.type.Types.DOUBLE;
60
- import static org.embulk.spi.type.Types.JSON;
61
- import static org.embulk.spi.type.Types.LONG;
62
- import static org.embulk.spi.type.Types.STRING;
63
- import static org.embulk.spi.type.Types.TIMESTAMP;
64
-
65
54
  public class TdInputPlugin
66
- implements InputPlugin
67
- {
68
- public interface PluginTask
69
- extends Task
70
- {
71
- @Config("apikey")
72
- public String getApiKey();
73
-
74
- @Config("endpoint")
75
- @ConfigDefault("\"api.treasuredata.com\"")
76
- public String getEndpoint();
77
-
78
- @Config("use_ssl")
79
- @ConfigDefault("true")
80
- public boolean getUseSsl();
81
-
82
- @Config("http_proxy")
83
- @ConfigDefault("null")
84
- public Optional<HttpProxyTask> getHttpProxy();
85
-
86
- // TODO timeout
87
- // TODO query, database
88
-
89
- @Config("query")
90
- @ConfigDefault("null")
91
- public Optional<String> getQuery();
92
-
93
- @Config("database")
94
- @ConfigDefault("null")
95
- public Optional<String> getDatabase();
96
-
97
- @Config("job_id")
98
- @ConfigDefault("null")
99
- public Optional<String> getJobId();
100
-
101
- @Config("stop_on_invalid_record")
102
- @ConfigDefault("false")
103
- public boolean getStopOnInvalidRecord();
104
-
105
- // TODO column_options
106
-
107
- @ConfigInject
108
- BufferAllocator getBufferAllocator();
109
- }
110
-
111
- public interface HttpProxyTask
112
- extends Task
113
- {
114
- @Config("host")
115
- public String getHost();
116
-
117
- @Config("port")
118
- public int getPort();
119
-
120
- @Config("use_ssl")
121
- @ConfigDefault("false")
122
- public boolean getUseSsl();
123
-
124
- @Config("user")
125
- @ConfigDefault("null")
126
- public Optional<String> getUser();
127
-
128
- @Config("password")
129
- @ConfigDefault("null")
130
- public Optional<String> getPassword();
131
- }
55
+ implements InputPlugin {
132
56
 
133
57
  private final Logger log;
134
58
 
135
59
  @Inject
136
- public TdInputPlugin()
137
- {
138
- this.log = getLogger(this.getClass());
60
+ public TdInputPlugin() {
61
+ this.log = Exec.getLogger(this.getClass());
62
+ }
63
+
64
+ private static JsonNode toJsonNode(final String schema) {
65
+ try {
66
+ return new ObjectMapper().readTree(schema);
67
+ } catch (IOException e) {
68
+ throw new ConfigException(String.format(Locale.ENGLISH,
69
+ "Failed to parse job result schema as JSON: %s", schema));
70
+ }
139
71
  }
140
72
 
141
73
  @Override
142
- public ConfigDiff transaction(ConfigSource config, InputPlugin.Control control)
143
- {
144
- PluginTask task = config.loadConfig(PluginTask.class);
145
- try (TDClient client = newTDClient(task)) {
146
- TDJob job = getTDJob(task, client);
74
+ public ConfigDiff transaction(final ConfigSource config, final InputPlugin.Control control) {
75
+ final PluginTask task = config.loadConfig(PluginTask.class);
76
+ try (final TDClient client = newTdClient(task)) {
77
+ final TDJob job = getTdJob(task, client);
147
78
 
148
- Optional<String> jobResultSchema = job.getResultSchema();
79
+ final Optional<String> jobResultSchema = job.getResultSchema();
149
80
  if (!jobResultSchema.isPresent()) {
150
- throw new ConfigException(String.format("Not found result schema of job %s", job.getJobId()));
81
+ throw new ConfigException(String.format(
82
+ "Not found result schema of job %s", job.getJobId()));
151
83
  }
152
84
 
153
- Schema inputSchema = convertSchema(job.getType(), toJsonNode(jobResultSchema.get()));
154
- newValueWriters(inputSchema); // validate if value writers can be created according to the input schema
85
+ final JsonNode jobResultSchemaJsonNode = toJsonNode(jobResultSchema.get());
86
+ final Schema inputSchema = convertSchema(job.getType(), jobResultSchemaJsonNode);
87
+ // validate if value writers can be created according to the input schema
88
+ newValueWriters(inputSchema);
155
89
 
156
- TaskSource taskSource = task.dump().set("job_id", job.getJobId()); // overwrite job_id
90
+ // overwrite job_id
91
+ final TaskSource taskSource = task.dump().set("job_id", job.getJobId());
157
92
  return resume(taskSource, inputSchema, 1, control);
158
93
  }
159
94
  }
160
95
 
161
- private TDClient newTDClient(PluginTask task)
162
- {
163
- TDClientBuilder builder = TDClient.newBuilder();
96
+ private TDClient newTdClient(final PluginTask task) {
97
+ final TDClientBuilder builder = TDClient.newBuilder();
164
98
  builder.setApiKey(task.getApiKey());
165
99
  builder.setEndpoint(task.getEndpoint());
166
100
  builder.setUseSSL(task.getUseSsl());
167
101
 
168
- Optional<ProxyConfig>proxyConfig = newProxyConfig(task.getHttpProxy());
102
+ final Optional<ProxyConfig> proxyConfig = newProxyConfig(task.getHttpProxy());
169
103
  if (proxyConfig.isPresent()) {
170
104
  builder.setProxy(proxyConfig.get());
171
105
  }
@@ -173,42 +107,48 @@ public class TdInputPlugin
173
107
  return builder.build();
174
108
  }
175
109
 
176
- private Optional<ProxyConfig> newProxyConfig(Optional<HttpProxyTask> task)
177
- {
178
- // This plugin searches http proxy settings and configures them to TDClient. The order of proxy setting searching is:
110
+ private Optional<ProxyConfig> newProxyConfig(final Optional<HttpProxyTask> task) {
111
+ // This plugin searches http proxy settings and configures them to TDClient.
112
+ // The order of proxy setting searching is:
179
113
  // 1. System properties
180
114
  // 2. http_proxy config option provided by this plugin
181
115
 
182
- Properties props = System.getProperties();
116
+ final Properties props = System.getProperties();
183
117
  if (props.containsKey("http.proxyHost") || props.containsKey("https.proxyHost")) {
184
- boolean useSsl = props.containsKey("https.proxyHost");
185
- String proto = !useSsl ? "http" : "https";
186
- String host = props.getProperty(proto + ".proxyHost");
187
- int port = parseInt(props.getProperty(proto + ".proxyPort", !useSsl ? "80" : "443"));
188
- Optional<String> user = fromNullable(props.getProperty(proto + ".proxyUser"));
189
- Optional<String> password = fromNullable(props.getProperty(proto + ".proxyPassword"));
118
+ final boolean useSsl = props.containsKey("https.proxyHost");
119
+ final String proto = !useSsl ? "http" : "https";
120
+ final String hostKey = proto + ".proxyHost";
121
+ final String portKey = proto + ".proxyPort";
122
+ final String userKey = proto + ".proxyUser";
123
+ final String passwordKey = proto + ".proxyPassword";
124
+ final String host = props.getProperty(hostKey);
125
+ final String defaultPort = !useSsl ? "80" : "443";
126
+ final int port = Integer.parseInt(props.getProperty(portKey, defaultPort));
127
+ final Optional<String> user = Optional.fromNullable(props.getProperty(userKey));
128
+ final Optional<String> password = Optional.fromNullable(props.getProperty(passwordKey));
190
129
  return Optional.of(new ProxyConfig(host, port, useSsl, user, password));
191
- }
192
- else if (task.isPresent()) {
193
- HttpProxyTask proxyTask = task.get();
194
- return Optional.of(new ProxyConfig(proxyTask.getHost(), proxyTask.getPort(), proxyTask.getUseSsl(),
195
- proxyTask.getUser(), proxyTask.getPassword()));
196
- }
197
- else {
130
+ } else if (task.isPresent()) {
131
+ final HttpProxyTask proxyTask = task.get();
132
+ final String host = proxyTask.getHost();
133
+ final int port = proxyTask.getPort();
134
+ final boolean useSsl = proxyTask.getUseSsl();
135
+ final Optional<String> user = proxyTask.getUser();
136
+ final Optional<String> password = proxyTask.getPassword();
137
+ return Optional.of(new ProxyConfig(host, port, useSsl, user, password));
138
+ } else {
198
139
  return Optional.absent();
199
140
  }
200
141
  }
201
142
 
202
- private TDJob getTDJob(PluginTask task, TDClient client)
203
- {
204
- String jobId;
143
+ private TDJob getTdJob(final PluginTask task, final TDClient client) {
144
+ final String jobId;
205
145
  if (!task.getJobId().isPresent()) {
206
146
  if (!task.getQuery().isPresent() || !task.getDatabase().isPresent()) {
207
- throw new ConfigException("Must specify both of 'query' and 'database' options if 'job_id' option is not used.");
147
+ throw new ConfigException("Must specify both of 'query' and 'database' options "
148
+ + "if 'job_id' option is not used.");
208
149
  }
209
150
  jobId = submitJob(task, client);
210
- }
211
- else {
151
+ } else {
212
152
  jobId = task.getJobId().get();
213
153
  }
214
154
 
@@ -216,24 +156,23 @@ public class TdInputPlugin
216
156
  return client.jobInfo(jobId);
217
157
  }
218
158
 
219
- private String submitJob(PluginTask task, TDClient client)
220
- {
221
- String query = task.getQuery().get();
222
- String database = task.getDatabase().get();
159
+ private String submitJob(final PluginTask task, final TDClient client) {
160
+ final String query = task.getQuery().get();
161
+ final String database = task.getDatabase().get();
223
162
 
224
- log.info(String.format(ENGLISH, "Submit a query for database '%s': %s", database, query));
225
- String jobId = client.submit(TDJobRequest.newPrestoQuery(database, query));
226
- log.info(String.format(ENGLISH, "Job %s is queued.", jobId));
163
+ log.info(String.format(Locale.ENGLISH,
164
+ "Submit a query for database '%s': %s", database, query));
165
+ final String jobId = client.submit(TDJobRequest.newPrestoQuery(database, query));
166
+ log.info(String.format(Locale.ENGLISH, "Job %s is queued.", jobId));
227
167
  return jobId;
228
168
  }
229
169
 
230
- private void waitJobCompletion(PluginTask task, TDClient client, String jobId)
231
- {
170
+ private void waitJobCompletion(final PluginTask task, final TDClient client, String jobId) {
232
171
  TDJobSummary js;
233
172
  long waitTime = 5 * 1000; // 5 secs
234
173
 
235
174
  // wait for job finish
236
- log.info(String.format(ENGLISH, "Confirm that job %s finished", jobId));
175
+ log.info(String.format(Locale.ENGLISH, "Confirm that job %s finished", jobId));
237
176
  while (true) {
238
177
  js = client.jobStatus(jobId);
239
178
  if (js.getStatus().isFinished()) {
@@ -243,93 +182,154 @@ public class TdInputPlugin
243
182
  log.debug("Wait for job finished");
244
183
  try {
245
184
  Thread.sleep(waitTime);
246
- }
247
- catch (InterruptedException ignored) {
185
+ } catch (InterruptedException ignored) {
186
+ // can ignore
248
187
  }
249
188
  }
250
189
 
251
190
  // confirm if the job status is 'success'
252
191
  if (js.getStatus() != TDJob.Status.SUCCESS) {
253
- throw new ConfigException(String.format(ENGLISH, "Cannot download job result because the job was '%s'.", js.getStatus()));
254
- }
255
- }
256
-
257
- private static JsonNode toJsonNode(String schema)
258
- {
259
- try {
260
- return new ObjectMapper().readTree(schema);
261
- }
262
- catch (IOException e) {
263
- throw new ConfigException(String.format(ENGLISH, "Failed to parse job result schema as JSON: %s", schema));
192
+ throw new ConfigException(String.format(Locale.ENGLISH,
193
+ "Cannot download job result because the job was '%s'.",
194
+ js.getStatus()));
264
195
  }
265
196
  }
266
197
 
267
- private Schema convertSchema(TDJob.Type jobType, JsonNode from)
268
- {
269
- Schema.Builder schema = new Schema.Builder();
270
- ArrayNode a = (ArrayNode) from;
198
+ private Schema convertSchema(final TDJob.Type jobType, final JsonNode from) {
199
+ final Schema.Builder schema = new Schema.Builder();
200
+ final ArrayNode a = (ArrayNode) from;
271
201
  for (int i = 0; i < a.size(); i++) {
272
- ArrayNode column = (ArrayNode)a.get(i);
273
- String name = column.get(0).asText();
274
- Type type = convertColumnType(jobType, column.get(1).asText());
202
+ final ArrayNode column = (ArrayNode) a.get(i);
203
+ final String name = column.get(0).asText();
204
+ final Type type = convertColumnType(jobType, column.get(1).asText());
275
205
  schema.add(name, type);
276
206
  }
277
207
  return schema.build();
278
208
  }
279
209
 
280
- private Type convertColumnType(TDJob.Type jobType, String from)
281
- {
210
+ private Type convertColumnType(final TDJob.Type jobType, final String from) {
282
211
  switch (jobType) {
283
- case PRESTO:
284
- return convertPrestoColumnType(from);
285
- case HIVE:
286
- default:
287
- throw new ConfigException(String.format(ENGLISH, "Unsupported job type '%s'. Supported types are [presto].", jobType)); // TODO hive
212
+ case PRESTO:
213
+ return convertPrestoColumnType(new Parser(from));
214
+ case HIVE:
215
+ default:
216
+ throw new ConfigException(String.format(Locale.ENGLISH,
217
+ "Unsupported job type '%s'. Supported types are [presto].",
218
+ jobType)); // TODO hive
288
219
  }
289
220
  }
290
221
 
291
- private Type convertPrestoColumnType(String from)
292
- {
293
- String t = from.toUpperCase(ENGLISH);
294
- if (t.equals("BOOLEAN")) {
295
- return BOOLEAN;
222
+ private Type convertPrestoColumnType(final Parser p) {
223
+ if (p.scan("BOOLEAN")) {
224
+ return Types.BOOLEAN;
225
+ } else if (p.scan("INTEGER")) {
226
+ return Types.LONG;
227
+ } else if (p.scan("BIGINT")) {
228
+ return Types.LONG;
229
+ } else if (p.scan("DOUBLE")) {
230
+ return Types.DOUBLE;
231
+ } else if (p.scan("DECIMAL")) {
232
+ return Types.DOUBLE;
233
+ } else if (p.scan("VARCHAR")) {
234
+ // TODO VARCHAR(n)
235
+ return Types.STRING;
236
+ } else if (p.scan("ARRAY")) {
237
+ if (!p.scan("(")) {
238
+ throw new IllegalArgumentException(
239
+ "Cannot parse type: expected '(' for array type: " + p.getOriginalString());
240
+ }
241
+ convertPrestoColumnType(p);
242
+ if (!p.scan(")")) {
243
+ throw new IllegalArgumentException(
244
+ "Cannot parse type: expected ')' for array type: " + p.getOriginalString());
245
+ }
246
+ return Types.JSON;
247
+ } else if (p.scan("MAP")) {
248
+ if (!p.scan("(")) {
249
+ throw new IllegalArgumentException(
250
+ "Cannot parse type: expected '(' for map type: " + p.getOriginalString());
251
+ }
252
+ convertPrestoColumnType(p);
253
+ if (!p.scan(",")) {
254
+ throw new IllegalArgumentException(
255
+ "Cannot parse type: expected ',' for map type: " + p.getOriginalString());
256
+ }
257
+ convertPrestoColumnType(p);
258
+ if (!p.scan(")")) {
259
+ throw new IllegalArgumentException(
260
+ "Cannot parse type: expected ')' for map type: " + p.getOriginalString());
261
+ }
262
+ return Types.JSON;
263
+ } else {
264
+ throw new ConfigException(String.format(Locale.ENGLISH,
265
+ "Unsupported presto type '%s'", p.getOriginalString())); // TODO other types
296
266
  }
297
- else if (t.equals("BIGINT")) {
298
- return LONG;
267
+ }
268
+
269
+ private static class Parser {
270
+ private final String original;
271
+ private final String string;
272
+ private int offset;
273
+
274
+ public Parser(final String original) {
275
+ this.original = original;
276
+ this.string = original.toUpperCase(Locale.ENGLISH);
299
277
  }
300
- else if (t.equals("DOUBLE") || t.equals("DECIMAL") || t.startsWith("DECIMAL")) {
301
- return DOUBLE;
278
+
279
+ public String getOriginalString() {
280
+ return original;
302
281
  }
303
- else if (t.equals("VARCHAR") || t.startsWith("VARCHAR")) {
304
- return STRING;
282
+
283
+ public String getString() {
284
+ return string;
305
285
  }
306
- else {
307
- throw new ConfigException(String.format(ENGLISH, "Unsupported presto type '%s'", from)); // TODO other types
286
+
287
+ public boolean scan(final String s) {
288
+ skipSpaces();
289
+ if (string.startsWith(s, offset)) {
290
+ offset += s.length();
291
+ return true;
292
+ }
293
+ return false;
294
+ }
295
+
296
+ public boolean eof() {
297
+ skipSpaces();
298
+ return string.length() <= offset;
299
+ }
300
+
301
+ private void skipSpaces() {
302
+ while (string.startsWith(" ", offset)) {
303
+ offset++;
304
+ }
308
305
  }
309
306
  }
310
307
 
311
308
  @Override
312
- public ConfigDiff resume(TaskSource taskSource,
313
- Schema schema, int taskCount,
314
- InputPlugin.Control control)
315
- {
309
+ public ConfigDiff resume(
310
+ final TaskSource taskSource,
311
+ final Schema schema,
312
+ final int taskCount,
313
+ final InputPlugin.Control control) {
316
314
  control.run(taskSource, schema, taskCount);
317
- return newConfigDiff();
315
+ return Exec.newConfigDiff();
318
316
  }
319
317
 
320
318
  @Override
321
- public void cleanup(TaskSource taskSource,
322
- Schema schema, int taskCount,
323
- List<TaskReport> successTaskReports)
324
- {
319
+ public void cleanup(
320
+ final TaskSource taskSource,
321
+ final Schema schema,
322
+ final int taskCount,
323
+ final List<TaskReport> successTaskReports) {
325
324
  // do nothing
326
325
  }
327
326
 
328
327
  @Override
329
- public TaskReport run(TaskSource taskSource,
330
- final Schema schema, int taskIndex,
331
- PageOutput output)
332
- {
328
+ public TaskReport run(
329
+ final TaskSource taskSource,
330
+ final Schema schema,
331
+ final int taskIndex,
332
+ final PageOutput output) {
333
333
  final PluginTask task = taskSource.loadTask(PluginTask.class);
334
334
  final BufferAllocator allocator = task.getBufferAllocator();
335
335
  final ValueWriter[] writers = newValueWriters(schema);
@@ -337,29 +337,33 @@ public class TdInputPlugin
337
337
  final boolean stopOnInvalidRecord = task.getStopOnInvalidRecord();
338
338
 
339
339
  try (final PageBuilder pageBuilder = new PageBuilder(allocator, schema, output);
340
- final TDClient client = newTDClient(task)) {
341
- client.jobResult(jobId, MESSAGE_PACK_GZ, new Function<InputStream, Void>() {
340
+ final TDClient client = newTdClient(task)) {
341
+ final TDResultFormat resultFormat = TDResultFormat.MESSAGE_PACK_GZ;
342
+ client.jobResult(jobId, resultFormat, new Function<InputStream, Void>() {
342
343
  @Override
343
- public Void apply(InputStream input)
344
- {
345
- try (MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(new GZIPInputStream(input))) {
344
+ public Void apply(InputStream input) {
345
+ try (final MessageUnpacker unpacker = MessagePack
346
+ .newDefaultUnpacker(new GZIPInputStream(input))) {
346
347
  while (unpacker.hasNext()) {
347
348
  try {
348
- Value v;
349
+ final Value v;
349
350
  try {
350
351
  v = unpacker.unpackValue();
351
- }
352
- catch (IOException e) {
352
+ } catch (IOException e) {
353
353
  throw new InvalidRecordException("Cannot unpack value", e);
354
354
  }
355
355
 
356
356
  if (!v.isArrayValue()) {
357
- throw new InvalidRecordException(String.format(ENGLISH, "Must be array value: ", v.toString()));
357
+ throw new InvalidRecordException(
358
+ String.format(Locale.ENGLISH,
359
+ "Must be array value: (%s)", v.toString()));
358
360
  }
359
361
 
360
- ArrayValue record = v.asArrayValue();
362
+ final ArrayValue record = v.asArrayValue();
361
363
  if (record.size() != schema.size()) {
362
- throw new InvalidRecordException(String.format(ENGLISH, "The size (%d) of the record is invalid", record.size()));
364
+ throw new InvalidRecordException(String.format(Locale.ENGLISH,
365
+ "The size (%d) of the record is invalid",
366
+ record.size()));
363
367
  }
364
368
 
365
369
  // write records to the page
@@ -368,16 +372,16 @@ public class TdInputPlugin
368
372
  }
369
373
 
370
374
  pageBuilder.addRecord();
371
- }
372
- catch (InvalidRecordException e) {
375
+ } catch (InvalidRecordException e) {
373
376
  if (stopOnInvalidRecord) {
374
- throw new DataException(String.format(ENGLISH, "Invalid record (%s)", e.getMessage()), e);
377
+ throw new DataException(String.format(Locale.ENGLISH,
378
+ "Invalid record (%s)", e.getMessage()), e);
375
379
  }
376
- log.warn(String.format(ENGLISH, "Skipped record (%s)", e.getMessage()));
380
+ log.warn(String.format(Locale.ENGLISH,
381
+ "Skipped record (%s)", e.getMessage()));
377
382
  }
378
383
  }
379
- }
380
- catch (IOException e) {
384
+ } catch (IOException e) {
381
385
  throw Throwables.propagate(e);
382
386
  }
383
387
 
@@ -388,58 +392,116 @@ public class TdInputPlugin
388
392
  pageBuilder.finish();
389
393
  }
390
394
 
391
- return newTaskReport();
395
+ return Exec.newTaskReport();
392
396
  }
393
397
 
394
- private ValueWriter[] newValueWriters(Schema schema)
395
- {
396
- ValueWriter[] writers = new ValueWriter[schema.size()];
398
+ private ValueWriter[] newValueWriters(final Schema schema) {
399
+ final ValueWriter[] writers = new ValueWriter[schema.size()];
397
400
  for (int i = 0; i < schema.size(); i++) {
398
401
  writers[i] = newValueWriter(schema.getColumn(i));
399
402
  }
400
403
  return writers;
401
404
  }
402
405
 
403
- private ValueWriter newValueWriter(Column column)
404
- {
405
- Type type = column.getType();
406
- if (type.equals(BOOLEAN)) {
406
+ private ValueWriter newValueWriter(final Column column) {
407
+ final Type type = column.getType();
408
+ if (type.equals(Types.BOOLEAN)) {
407
409
  return new BooleanValueWriter(column);
408
- }
409
- else if (type.equals(DOUBLE)) {
410
+ } else if (type.equals(Types.DOUBLE)) {
410
411
  return new DoubleValueWriter(column);
411
- }
412
- else if (type.equals(JSON)) {
413
- throw new ConfigException(String.format(ENGLISH, "Unsupported column type (%s:%s)", column.getName(), type)); // TODO
414
- }
415
- else if (type.equals(LONG)) {
412
+ } else if (type.equals(Types.JSON)) {
413
+ return new JsonValueWriter(column);
414
+ } else if (type.equals(Types.LONG)) {
416
415
  return new LongValueWriter(column);
417
- }
418
- else if (type.equals(STRING)) {
416
+ } else if (type.equals(Types.STRING)) {
419
417
  return new StringValueWriter(column);
420
- }
421
- else if (type.equals(TIMESTAMP)) {
422
- throw new ConfigException(String.format(ENGLISH, "Unsupported column type (%s:%s)", column.getName(), type)); // TODO
423
- }
424
- else {
425
- throw new ConfigException(String.format(ENGLISH, "Unsupported column type (%s:%s)", column.getName(), type)); // TODO
418
+ } else if (type.equals(Types.TIMESTAMP)) {
419
+ throw new ConfigException(String.format(Locale.ENGLISH,
420
+ "Unsupported column type (%s:%s)", column.getName(), type)); // TODO
421
+ } else {
422
+ throw new ConfigException(String.format(Locale.ENGLISH,
423
+ "Unsupported column type (%s:%s)", column.getName(), type)); // TODO
426
424
  }
427
425
  }
428
426
 
429
427
  @Override
430
- public ConfigDiff guess(ConfigSource config)
431
- {
432
- return newConfigDiff(); // do nothing
428
+ public ConfigDiff guess(final ConfigSource config) {
429
+ return Exec.newConfigDiff(); // do nothing
430
+ }
431
+
432
+ public interface PluginTask
433
+ extends Task {
434
+
435
+ @Config("apikey")
436
+ public String getApiKey();
437
+
438
+ @Config("endpoint")
439
+ @ConfigDefault("\"api.treasuredata.com\"")
440
+ public String getEndpoint();
441
+
442
+ @Config("use_ssl")
443
+ @ConfigDefault("true")
444
+ public boolean getUseSsl();
445
+
446
+ @Config("http_proxy")
447
+ @ConfigDefault("null")
448
+ public Optional<HttpProxyTask> getHttpProxy();
449
+
450
+ // TODO timeout
451
+ // TODO query, database
452
+
453
+ @Config("query")
454
+ @ConfigDefault("null")
455
+ public Optional<String> getQuery();
456
+
457
+ @Config("database")
458
+ @ConfigDefault("null")
459
+ public Optional<String> getDatabase();
460
+
461
+ @Config("job_id")
462
+ @ConfigDefault("null")
463
+ public Optional<String> getJobId();
464
+
465
+ @Config("stop_on_invalid_record")
466
+ @ConfigDefault("false")
467
+ public boolean getStopOnInvalidRecord();
468
+
469
+ // TODO column_options
470
+
471
+ @ConfigInject
472
+ BufferAllocator getBufferAllocator();
473
+ }
474
+
475
+ public interface HttpProxyTask
476
+ extends Task {
477
+
478
+ @Config("host")
479
+ public String getHost();
480
+
481
+ @Config("port")
482
+ public int getPort();
483
+
484
+ @Config("use_ssl")
485
+ @ConfigDefault("false")
486
+ public boolean getUseSsl();
487
+
488
+ @Config("user")
489
+ @ConfigDefault("null")
490
+ public Optional<String> getUser();
491
+
492
+ @Config("password")
493
+ @ConfigDefault("null")
494
+ public Optional<String> getPassword();
433
495
  }
434
496
 
435
497
  static class InvalidRecordException
436
- extends RuntimeException
437
- {
438
- InvalidRecordException(String cause) {
498
+ extends RuntimeException {
499
+
500
+ InvalidRecordException(final String cause) {
439
501
  super(cause);
440
502
  }
441
503
 
442
- InvalidRecordException(String cause, Throwable t) {
504
+ InvalidRecordException(final String cause, final Throwable t) {
443
505
  super(cause, t);
444
506
  }
445
507
  }