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.
- checksums.yaml +4 -4
- data/.travis.yml +6 -0
- data/CHANGELOG.md +5 -0
- data/build.gradle +34 -10
- data/config/checkstyle/README.md +6 -0
- data/config/checkstyle/checkstyle-suppressions.xml +8 -0
- data/config/checkstyle/checkstyle.xml +195 -104
- data/config/checkstyle/google_checks.xml +218 -0
- data/embulk-input-td.gemspec +1 -1
- data/src/main/java/org/embulk/input/td/TdInputPlugin.java +301 -239
- data/src/main/java/org/embulk/input/td/writer/AbstractValueWriter.java +5 -8
- data/src/main/java/org/embulk/input/td/writer/BooleanValueWriter.java +4 -6
- data/src/main/java/org/embulk/input/td/writer/DoubleValueWriter.java +4 -6
- data/src/main/java/org/embulk/input/td/writer/JsonValueWriter.java +4 -6
- data/src/main/java/org/embulk/input/td/writer/LongValueWriter.java +4 -6
- data/src/main/java/org/embulk/input/td/writer/StringValueWriter.java +4 -6
- data/src/main/java/org/embulk/input/td/writer/ValueWriter.java +2 -2
- data/src/test/java/org/embulk/input/td/TestTdInputPlugin.java +1 -2
- metadata +7 -4
- data/config/checkstyle/default.xml +0 -108
    
        data/embulk-input-td.gemspec
    CHANGED
    
    
| @@ -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  | 
| 23 | 
            -
            import  | 
| 24 | 
            -
            import  | 
| 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 | 
            -
             | 
| 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 | 
            -
                     | 
| 145 | 
            -
             | 
| 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( | 
| 81 | 
            +
                            throw new ConfigException(String.format(
         | 
| 82 | 
            +
                                    "Not found result schema of job %s", job.getJobId()));
         | 
| 151 83 | 
             
                        }
         | 
| 152 84 |  | 
| 153 | 
            -
                         | 
| 154 | 
            -
                         | 
| 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 | 
            -
                         | 
| 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  | 
| 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 | 
            -
                    //  | 
| 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  | 
| 187 | 
            -
                         | 
| 188 | 
            -
                         | 
| 189 | 
            -
                         | 
| 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 | 
            -
             | 
| 193 | 
            -
                         | 
| 194 | 
            -
                         | 
| 195 | 
            -
             | 
| 196 | 
            -
             | 
| 197 | 
            -
             | 
| 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  | 
| 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  | 
| 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  | 
| 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, | 
| 225 | 
            -
             | 
| 226 | 
            -
                     | 
| 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 | 
            -
             | 
| 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, | 
| 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 | 
            -
                     | 
| 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 | 
            -
             | 
| 284 | 
            -
             | 
| 285 | 
            -
             | 
| 286 | 
            -
             | 
| 287 | 
            -
             | 
| 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( | 
| 292 | 
            -
             | 
| 293 | 
            -
             | 
| 294 | 
            -
                    if ( | 
| 295 | 
            -
                        return  | 
| 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 | 
            -
             | 
| 298 | 
            -
             | 
| 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 | 
            -
             | 
| 301 | 
            -
             | 
| 278 | 
            +
             | 
| 279 | 
            +
                    public String getOriginalString() {
         | 
| 280 | 
            +
                        return original;
         | 
| 302 281 | 
             
                    }
         | 
| 303 | 
            -
             | 
| 304 | 
            -
             | 
| 282 | 
            +
             | 
| 283 | 
            +
                    public String getString() {
         | 
| 284 | 
            +
                        return string;
         | 
| 305 285 | 
             
                    }
         | 
| 306 | 
            -
             | 
| 307 | 
            -
             | 
| 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( | 
| 313 | 
            -
                         | 
| 314 | 
            -
                         | 
| 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( | 
| 322 | 
            -
                         | 
| 323 | 
            -
                         | 
| 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( | 
| 330 | 
            -
                        final  | 
| 331 | 
            -
                         | 
| 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 | 
            -
             | 
| 341 | 
            -
                         | 
| 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 | 
            -
             | 
| 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( | 
| 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, | 
| 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, | 
| 377 | 
            +
                                                throw new DataException(String.format(Locale.ENGLISH,
         | 
| 378 | 
            +
                                                        "Invalid record (%s)", e.getMessage()), e);
         | 
| 375 379 | 
             
                                            }
         | 
| 376 | 
            -
                                            log.warn(String.format(ENGLISH, | 
| 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 | 
            -
                     | 
| 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 | 
            -
             | 
| 413 | 
            -
             | 
| 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 | 
            -
             | 
| 422 | 
            -
             | 
| 423 | 
            -
                    }
         | 
| 424 | 
            -
             | 
| 425 | 
            -
             | 
| 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 | 
            -
             | 
| 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 | 
             
                }
         |