rows) {
+ this.rows = Lists.newLinkedList(checkNotNull(rows));
+ return this;
+ }
+
+ /**
+ * Adds a row to be inserted.
+ */
+ public Builder addRow(RowToInsert rowToInsert) {
+ checkNotNull(rowToInsert);
+ if (rows == null) {
+ rows = Lists.newArrayList();
+ }
+ rows.add(rowToInsert);
+ return this;
+ }
+
+ /**
+ * Adds a row to be inserted with associated id.
+ *
+ * Example usage of adding a row with associated id:
+ *
{@code
+ * InsertAllRequest.Builder builder = InsertAllRequest.builder(tableId);
+ * List repeatedFieldValue = Arrays.asList(1L, 2L);
+ * Map recordContent = new HashMap();
+ * recordContent.put("subfieldName1", "value");
+ * recordContent.put("subfieldName2", repeatedFieldValue);
+ * Map rowContent = new HashMap();
+ * rowContent.put("fieldName1", true);
+ * rowContent.put("fieldName2", recordContent);
+ * builder.addRow("rowId", rowContent);
+ * }
+ */
+ public Builder addRow(String id, Map content) {
+ addRow(new RowToInsert(id, content));
+ return this;
+ }
+
+ /**
+ * Adds a row to be inserted without an associated id.
+ *
+ * Example usage of adding a row without an associated id:
+ *
{@code
+ * InsertAllRequest.Builder builder = InsertAllRequest.builder(tableId);
+ * List repeatedFieldValue = Arrays.asList(1L, 2L);
+ * Map recordContent = new HashMap();
+ * recordContent.put("subfieldName1", "value");
+ * recordContent.put("subfieldName2", repeatedFieldValue);
+ * Map rowContent = new HashMap();
+ * rowContent.put("fieldName1", true);
+ * rowContent.put("fieldName2", recordContent);
+ * builder.addRow(rowContent);
+ * }
+ */
+ public Builder addRow(Map content) {
+ addRow(new RowToInsert(null, content));
+ return this;
+ }
+
+ /**
+ * Sets whether to insert all valid rows of a request, even if invalid rows exist. If not set
+ * the entire insert request will fail if it contains an invalid row.
+ */
+ public Builder skipInvalidRows(boolean skipInvalidRows) {
+ this.skipInvalidRows = skipInvalidRows;
+ return this;
+ }
+
+ /**
+ * Sets whether to accept rows that contain values that do not match the schema. The unknown
+ * values are ignored. If not set, rows with unknown values are considered to be invalid.
+ */
+ public Builder ignoreUnknownValues(boolean ignoreUnknownValues) {
+ this.ignoreUnknownValues = ignoreUnknownValues;
+ return this;
+ }
+
+ /**
+ * If specified, the destination table is treated as a base template. Rows are inserted into an
+ * instance table named "{destination}{templateSuffix}". BigQuery will manage the creation of
+ * the instance table, using the schema of the base template table. Table creation might take
+ * some time. To obtain table's information after {@link BigQuery#insertAll(InsertAllRequest)}
+ * is called use:
+ * {@code
+ * String suffixTableId = ...;
+ * BaseTableInfo suffixTable = bigquery.getTable(DATASET, suffixTableId);
+ * while (suffixTable == null) {
+ * Thread.sleep(1000L);
+ * suffixTable = bigquery.getTable(DATASET, suffixTableId);
+ * }}
+ *
+ * @see
+ * Template Tables
+ */
+ public Builder templateSuffix(String templateSuffix) {
+ this.templateSuffix = templateSuffix;
+ return this;
+ }
+
+ public InsertAllRequest build() {
+ return new InsertAllRequest(this);
+ }
+ }
+
+ private InsertAllRequest(Builder builder) {
+ this.table = checkNotNull(builder.table);
+ this.rows = ImmutableList.copyOf(checkNotNull(builder.rows));
+ this.ignoreUnknownValues = builder.ignoreUnknownValues;
+ this.skipInvalidRows = builder.skipInvalidRows;
+ this.templateSuffix = builder.templateSuffix;
+ }
+
+ /**
+ * Returns the destination table for rows insert request.
+ */
+ public TableId table() {
+ return table;
+ }
+
+ /**
+ * Returns the rows to be inserted.
+ */
+ public List rows() {
+ return rows;
+ }
+
+ /**
+ * Returns whether to accept rows that contain values that do not match the schema. The unknown
+ * values are ignored. If not set, rows with unknown values are considered to be invalid.
+ */
+ public Boolean ignoreUnknownValues() {
+ return ignoreUnknownValues;
+ }
+
+ /**
+ * Returns whether to insert all valid rows of a request, even if invalid rows exist. If not set
+ * the entire insert request will fail if it contains an invalid row.
+ */
+ public Boolean skipInvalidRows() {
+ return skipInvalidRows;
+ }
+
+ /**
+ * If specified, the destination table is treated as a base template. Rows are inserted into an
+ * instance table named "{destination}{templateSuffix}". BigQuery will manage the creation of the
+ * instance table, using the schema of the base template table. Table creation might take some
+ * time. To obtain table's information after {@link BigQuery#insertAll(InsertAllRequest)} is
+ * called use:
+ * {@code
+ * String suffixTableId = ...;
+ * BaseTableInfo suffixTable = bigquery.getTable(DATASET, suffixTableId);
+ * while (suffixTable == null) {
+ * Thread.sleep(1000L);
+ * suffixTable = bigquery.getTable(DATASET, suffixTableId);
+ * }}
+ *
+ * @see
+ * Template Tables
+ */
+ public String templateSuffix() {
+ return templateSuffix;
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table.
+ */
+ public static Builder builder(TableId table) {
+ return new Builder().table(table);
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table and the
+ * rows to insert.
+ */
+ public static Builder builder(TableId table, Iterable rows) {
+ return builder(table).rows(rows);
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table and the
+ * rows to insert.
+ */
+ public static Builder builder(TableId table, RowToInsert... rows) {
+ return builder(table, ImmutableList.copyOf(rows));
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table.
+ */
+ public static Builder builder(String datasetId, String tableId) {
+ return new Builder().table(TableId.of(datasetId, tableId));
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table and the
+ * rows to insert.
+ */
+ public static Builder builder(String datasetId, String tableId, Iterable rows) {
+ return builder(TableId.of(datasetId, tableId), rows);
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table and the
+ * rows to insert.
+ */
+ public static Builder builder(String datasetId, String tableId, RowToInsert... rows) {
+ return builder(TableId.of(datasetId, tableId), rows);
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table and the
+ * rows to insert.
+ */
+ public static Builder builder(BaseTableInfo tableInfo, Iterable rows) {
+ return builder(tableInfo.tableId(), rows);
+ }
+
+ /**
+ * Returns a builder for an {@code InsertAllRequest} object given the destination table and the
+ * rows to insert.
+ */
+ public static Builder builder(BaseTableInfo tableInfo, RowToInsert... rows) {
+ return builder(tableInfo.tableId(), rows);
+ }
+
+ /**
+ * Returns a {@code InsertAllRequest} object given the destination table and the rows to insert.
+ */
+ public static InsertAllRequest of(TableId tableId, Iterable rows) {
+ return builder(tableId, rows).build();
+ }
+
+ /**
+ * Returns a {@code InsertAllRequest} object given the destination table and the rows to insert.
+ */
+ public static InsertAllRequest of(TableId tableId, RowToInsert... rows) {
+ return builder(tableId, rows).build();
+ }
+
+ /**
+ * Returns a {@code InsertAllRequest} object given the destination table and the rows to insert.
+ */
+ public static InsertAllRequest of(String datasetId, String tableId, Iterable rows) {
+ return builder(datasetId, tableId, rows).build();
+ }
+
+ /**
+ * Returns a {@code InsertAllRequest} object given the destination table and the rows to insert.
+ */
+ public static InsertAllRequest of(String datasetId, String tableId, RowToInsert... rows) {
+ return builder(datasetId, tableId, rows).build();
+ }
+
+ /**
+ * Returns a {@code InsertAllRequest} object given the destination table and the rows to insert.
+ */
+ public static InsertAllRequest of(BaseTableInfo tableInfo, Iterable rows) {
+ return builder(tableInfo.tableId(), rows).build();
+ }
+
+ /**
+ * Returns a {@code InsertAllRequest} object given the destination table and the rows to insert.
+ */
+ public static InsertAllRequest of(BaseTableInfo tableInfo, RowToInsert... rows) {
+ return builder(tableInfo.tableId(), rows).build();
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("table", table)
+ .add("rows", rows)
+ .add("ignoreUnknownValues", ignoreUnknownValues)
+ .add("skipInvalidRows", skipInvalidRows)
+ .add("templateSuffix", templateSuffix)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(table, rows, ignoreUnknownValues, skipInvalidRows, templateSuffix);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof InsertAllRequest)) {
+ return false;
+ }
+ InsertAllRequest other = (InsertAllRequest) obj;
+ return Objects.equals(table, other.table)
+ && Objects.equals(rows, other.rows)
+ && Objects.equals(ignoreUnknownValues, other.ignoreUnknownValues)
+ && Objects.equals(skipInvalidRows, other.skipInvalidRows)
+ && Objects.equals(templateSuffix, other.templateSuffix);
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/InsertAllResponse.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/InsertAllResponse.java
new file mode 100644
index 000000000000..992c5d851bbc
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/InsertAllResponse.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gcloud.bigquery;
+
+import com.google.api.services.bigquery.model.ErrorProto;
+import com.google.api.services.bigquery.model.TableDataInsertAllResponse;
+import com.google.api.services.bigquery.model.TableDataInsertAllResponse.InsertErrors;
+import com.google.common.base.Function;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Google Cloud BigQuery insert all response. Objects of this class possibly contain errors for an
+ * {@link InsertAllRequest}. If a row failed to be inserted, the non-empty list of errors associated
+ * to that row's index can be obtained with {@link InsertAllResponse#errorsFor(long)}.
+ * {@link InsertAllResponse#insertErrors()} can be used to return all errors caused by a
+ * {@link InsertAllRequest} as a map.
+ */
+public class InsertAllResponse implements Serializable {
+
+ private static final long serialVersionUID = -6934152676514098452L;
+
+ private final Map> insertErrors;
+
+ InsertAllResponse(Map> insertErrors) {
+ this.insertErrors = insertErrors != null ? ImmutableMap.copyOf(insertErrors)
+ : ImmutableMap.>of();
+ }
+
+ /**
+ * Returns all insertion errors as a map whose keys are indexes of rows that failed to insert.
+ * Each failed row index is associated with a non-empty list of {@link BigQueryError}.
+ */
+ public Map> insertErrors() {
+ return insertErrors;
+ }
+
+ /**
+ * Returns errors for the provided row index. If no error exists returns {@code null}.
+ */
+ public List errorsFor(long index) {
+ return insertErrors.get(index);
+ }
+
+ /**
+ * Returns {@code true} if no row insertion failed, {@code false} otherwise. If {@code false}
+ * {@link #insertErrors()} returns an empty map.
+ */
+ public boolean hasErrors() {
+ return !insertErrors.isEmpty();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(insertErrors);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof InsertAllResponse
+ && Objects.equals(insertErrors, ((InsertAllResponse) obj).insertErrors);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("insertErrors", insertErrors).toString();
+ }
+
+ TableDataInsertAllResponse toPb() {
+ TableDataInsertAllResponse responsePb = new TableDataInsertAllResponse();
+ if (!insertErrors.isEmpty()) {
+ responsePb.setInsertErrors(ImmutableList.copyOf(Iterables.transform(insertErrors.entrySet(),
+ new Function>, InsertErrors>() {
+ @Override
+ public InsertErrors apply(Map.Entry> entry) {
+ return new InsertErrors()
+ .setIndex(entry.getKey())
+ .setErrors(Lists.transform(entry.getValue(), BigQueryError.TO_PB_FUNCTION));
+ }
+ })));
+ }
+ return responsePb;
+ }
+
+ static InsertAllResponse fromPb(TableDataInsertAllResponse responsePb) {
+ Map> insertErrors = null;
+ if (responsePb.getInsertErrors() != null) {
+ List errorsPb = responsePb.getInsertErrors();
+ insertErrors = Maps.newHashMapWithExpectedSize(errorsPb.size());
+ for (InsertErrors errorPb : errorsPb) {
+ insertErrors.put(errorPb.getIndex(), Lists.transform(
+ errorPb.getErrors() != null ? errorPb.getErrors() : ImmutableList.of(),
+ BigQueryError.FROM_PB_FUNCTION));
+ }
+ }
+ return new InsertAllResponse(insertErrors);
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Job.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Job.java
new file mode 100644
index 000000000000..c0d7ddc29c37
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Job.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gcloud.bigquery;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A Google BigQuery Job.
+ *
+ * Objects of this class are immutable. To get a {@code Job} object with the most recent
+ * information use {@link #reload}.
+ *
+ */
+public final class Job {
+
+ private final BigQuery bigquery;
+ private final JobInfo info;
+
+ /**
+ * Constructs a {@code Job} object for the provided {@code JobInfo}. The BigQuery service
+ * is used to issue requests.
+ *
+ * @param bigquery the BigQuery service used for issuing requests
+ * @param info jobs's info
+ */
+ public Job(BigQuery bigquery, JobInfo info) {
+ this.bigquery = checkNotNull(bigquery);
+ this.info = checkNotNull(info);
+ }
+
+ /**
+ * Creates a {@code Job} object for the provided job's user-defined id. Performs an RPC call to
+ * get the latest job information.
+ *
+ * @param bigquery the BigQuery service used for issuing requests
+ * @param job job's id, either user-defined or picked by the BigQuery service
+ * @param options job options
+ * @return the {@code Job} object or {@code null} if not found
+ * @throws BigQueryException upon failure
+ */
+ public static Job get(BigQuery bigquery, String job, BigQuery.JobOption... options) {
+ JobInfo info = bigquery.getJob(job, options);
+ return info != null ? new Job(bigquery, info) : null;
+ }
+
+ /**
+ * Returns the job's information.
+ */
+ public JobInfo info() {
+ return info;
+ }
+
+ /**
+ * Checks if this job exists.
+ *
+ * @return {@code true} if this job exists, {@code false} otherwise
+ * @throws BigQueryException upon failure
+ */
+ public boolean exists() {
+ return bigquery.getJob(info.jobId(), BigQuery.JobOption.fields()) != null;
+ }
+
+ /**
+ * Checks if this job has completed its execution, either failing or succeeding. If the job does
+ * not exist this method returns {@code false}. To correctly wait for job's completion check that
+ * the job exists first, using {@link #exists()}:
+ * {@code
+ * if (job.exists()) {
+ * while(!job.isDone()) {
+ * Thread.sleep(1000L);
+ * }
+ * }}
+ *
+ * @return {@code true} if this job is in {@link JobStatus.State#DONE} state, {@code false} if the
+ * state is not {@link JobStatus.State#DONE} or the job does not exist
+ * @throws BigQueryException upon failure
+ */
+ public boolean isDone() {
+ JobInfo job = bigquery.getJob(info.jobId(),
+ BigQuery.JobOption.fields(BigQuery.JobField.STATUS));
+ return job != null && job.status().state() == JobStatus.State.DONE;
+ }
+
+ /**
+ * Fetches current job's latest information. Returns {@code null} if the job does not exist.
+ *
+ * @param options job options
+ * @return a {@code Job} object with latest information or {@code null} if not found
+ * @throws BigQueryException upon failure
+ */
+ public Job reload(BigQuery.JobOption... options) {
+ return Job.get(bigquery, info.jobId().job(), options);
+ }
+
+ /**
+ * Sends a job cancel request.
+ *
+ * @return {@code true} if cancel request was sent successfully, {@code false} if job was not
+ * found
+ * @throws BigQueryException upon failure
+ */
+ public boolean cancel() {
+ return bigquery.cancel(info.jobId());
+ }
+
+ /**
+ * Returns the job's {@code BigQuery} object used to issue requests.
+ */
+ public BigQuery bigquery() {
+ return bigquery;
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobConfiguration.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobConfiguration.java
new file mode 100644
index 000000000000..2244969567ef
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobConfiguration.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gcloud.bigquery;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Base class for a BigQuery job configuration.
+ */
+public abstract class JobConfiguration implements Serializable {
+
+ private static final long serialVersionUID = -548132177415406526L;
+
+ private final Type type;
+
+ /**
+ * Type of a BigQuery Job.
+ */
+ enum Type {
+ /**
+ * A Copy Job copies an existing table to another new or existing table. Instances of
+ * {@code JobConfiguration} for this type are implemented by {@link CopyJobConfiguration}.
+ */
+ COPY,
+ /**
+ * An Extract Job exports a BigQuery table to Google Cloud Storage. Instances of
+ * {@code JobConfiguration} for this type are implemented by {@link ExtractJobConfiguration}.
+ */
+ EXTRACT,
+ /**
+ * A Load Job loads data from one of several formats into a table. Instances of
+ * {@code JobConfiguration} for this type are implemented by {@link LoadJobConfiguration}.
+ */
+ LOAD,
+ /**
+ * A Query Job runs a query against BigQuery data. Instances of
+ * {@code JobConfiguration} for this type are implemented by {@link QueryJobConfiguration}.
+ */
+ QUERY
+ }
+
+ /**
+ * Base builder for job configurations.
+ *
+ * @param the job configuration type
+ * @param the job configuration builder
+ */
+ public abstract static class Builder> {
+
+ private Type type;
+
+ Builder(Type type) {
+ this.type = checkNotNull(type);
+ }
+
+ @SuppressWarnings("unchecked")
+ B self() {
+ return (B) this;
+ }
+
+ B type(Type type) {
+ this.type = checkNotNull(type);
+ return self();
+ }
+
+ /**
+ * Creates an object.
+ */
+ public abstract T build();
+ }
+
+ JobConfiguration(Builder builder) {
+ this.type = builder.type;
+ }
+
+ /**
+ * Returns the type of the job configuration.
+ */
+ public Type type() {
+ return type;
+ }
+
+ /**
+ * Returns a builder for the object.
+ */
+ public abstract Builder toBuilder();
+
+ ToStringHelper toStringHelper() {
+ return MoreObjects.toStringHelper(this).add("type", type);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper().toString();
+ }
+
+ final int baseHashCode() {
+ return Objects.hash(type);
+ }
+
+ final boolean baseEquals(JobConfiguration jobConfiguration) {
+ return Objects.equals(toPb(), jobConfiguration.toPb());
+ }
+
+ abstract JobConfiguration setProjectId(String projectId);
+
+ abstract com.google.api.services.bigquery.model.JobConfiguration toPb();
+
+ @SuppressWarnings("unchecked")
+ static T fromPb(
+ com.google.api.services.bigquery.model.JobConfiguration configurationPb) {
+ if (configurationPb.getCopy() != null) {
+ return (T) CopyJobConfiguration.fromPb(configurationPb);
+ } else if (configurationPb.getExtract() != null) {
+ return (T) ExtractJobConfiguration.fromPb(configurationPb);
+ } else if (configurationPb.getLoad() != null) {
+ return (T) LoadJobConfiguration.fromPb(configurationPb);
+ } else if (configurationPb.getQuery() != null) {
+ return (T) QueryJobConfiguration.fromPb(configurationPb);
+ } else {
+ // never reached
+ throw new IllegalArgumentException("Job configuration is not supported");
+ }
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobId.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobId.java
new file mode 100644
index 000000000000..898c894f9a21
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobId.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gcloud.bigquery;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.api.services.bigquery.model.JobReference;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Google BigQuery Job identity.
+ */
+public class JobId implements Serializable {
+
+ private static final long serialVersionUID = 1225914835379688976L;
+
+ private final String project;
+ private final String job;
+
+ /**
+ * Returns project's user-defined id.
+ */
+ public String project() {
+ return project;
+ }
+
+ /**
+ * Returns the job's user-defined id.
+ */
+ public String job() {
+ return job;
+ }
+
+ private JobId(String project, String job) {
+ this.project = project;
+ this.job = job;
+ }
+
+ /**
+ * Creates a job identity given project's and job's user-defined id.
+ */
+ public static JobId of(String project, String job) {
+ return new JobId(checkNotNull(project), checkNotNull(job));
+ }
+
+ /**
+ * Creates a job identity given only its user-defined id.
+ */
+ public static JobId of(String job) {
+ return new JobId(null, checkNotNull(job));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof JobId && Objects.equals(toPb(), ((JobId) obj).toPb());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(project, job);
+ }
+
+ @Override
+ public String toString() {
+ return toPb().toString();
+ }
+
+ JobReference toPb() {
+ return new JobReference().setProjectId(project).setJobId(job);
+ }
+
+ static JobId fromPb(JobReference jobRef) {
+ return new JobId(jobRef.getProjectId(), jobRef.getJobId());
+ }
+}
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobInfo.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobInfo.java
new file mode 100644
index 000000000000..47135b6d97d0
--- /dev/null
+++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobInfo.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gcloud.bigquery;
+
+import com.google.api.services.bigquery.model.Job;
+import com.google.common.base.Function;
+import com.google.common.base.MoreObjects;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Google BigQuery Job information. Jobs are objects that manage asynchronous tasks such as running
+ * queries, loading data, and exporting data. Use {@link CopyJobConfiguration} for a job that
+ * copies an existing table. Use {@link ExtractJobConfiguration} for a job that exports a table to
+ * Google Cloud Storage. Use {@link LoadJobConfiguration} for a job that loads data from Google
+ * Cloud Storage into a table. Use {@link QueryJobConfiguration} for a job that runs a query.
+ *
+ * @see Jobs
+ */
+public final class JobInfo implements Serializable {
+
+ static final Function FROM_PB_FUNCTION =
+ new Function() {
+ @Override
+ public JobInfo apply(Job pb) {
+ return JobInfo.fromPb(pb);
+ }
+ };
+ private static final long serialVersionUID = -3272941007234620265L;
+
+ /**
+ * Specifies whether the job is allowed to create new tables.
+ */
+ public enum CreateDisposition {
+ /**
+ * Configures the job to create the table if it does not exist.
+ */
+ CREATE_IF_NEEDED,
+
+ /**
+ * Configures the job to fail with a not-found error if the table does not exist.
+ */
+ CREATE_NEVER
+ }
+
+ /**
+ * Specifies the action that occurs if the destination table already exists.
+ */
+ public enum WriteDisposition {
+ /**
+ * Configures the job to overwrite the table data if table already exists.
+ */
+ WRITE_TRUNCATE,
+
+ /**
+ * Configures the job to append data to the table if it already exists.
+ */
+ WRITE_APPEND,
+
+ /**
+ * Configures the job to fail with a duplicate error if the table already exists.
+ */
+ WRITE_EMPTY
+ }
+
+ private final String etag;
+ private final String id;
+ private final JobId jobId;
+ private final String selfLink;
+ private final JobStatus status;
+ private final JobStatistics statistics;
+ private final String userEmail;
+ private final JobConfiguration configuration;
+
+ public static final class Builder {
+
+ private String etag;
+ private String id;
+ private JobId jobId;
+ private String selfLink;
+ private JobStatus status;
+ private JobStatistics statistics;
+ private String userEmail;
+ private JobConfiguration configuration;
+
+ private Builder() {}
+
+ private Builder(JobInfo jobInfo) {
+ this.etag = jobInfo.etag;
+ this.id = jobInfo.id;
+ this.jobId = jobInfo.jobId;
+ this.selfLink = jobInfo.selfLink;
+ this.status = jobInfo.status;
+ this.statistics = jobInfo.statistics;
+ this.userEmail = jobInfo.userEmail;
+ this.configuration = jobInfo.configuration;
+ }
+
+ protected Builder(Job jobPb) {
+ this.etag = jobPb.getEtag();
+ this.id = jobPb.getId();
+ if (jobPb.getJobReference() != null) {
+ this.jobId = JobId.fromPb(jobPb.getJobReference());
+ }
+ this.selfLink = jobPb.getSelfLink();
+ if (jobPb.getStatus() != null) {
+ this.status = JobStatus.fromPb(jobPb.getStatus());
+ }
+ if (jobPb.getStatistics() != null) {
+ this.statistics = JobStatistics.fromPb(jobPb.getStatistics());
+ }
+ this.userEmail = jobPb.getUserEmail();
+ this.configuration = JobConfiguration.fromPb(jobPb.getConfiguration());
+ }
+
+ Builder etag(String etag) {
+ this.etag = etag;
+ return this;
+ }
+
+ Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Sets the job identity.
+ */
+ public Builder jobId(JobId jobId) {
+ this.jobId = jobId;
+ return this;
+ }
+
+ Builder selfLink(String selfLink) {
+ this.selfLink = selfLink;
+ return this;
+ }
+
+ Builder status(JobStatus status) {
+ this.status = status;
+ return this;
+ }
+
+ Builder statistics(JobStatistics statistics) {
+ this.statistics = statistics;
+ return this;
+ }
+
+ Builder userEmail(String userEmail) {
+ this.userEmail = userEmail;
+ return this;
+ }
+
+ public Builder configuration(JobConfiguration configuration) {
+ this.configuration = configuration;
+ return this;
+ }
+
+ public JobInfo build() {
+ return new JobInfo(this);
+ }
+ }
+
+ private JobInfo(Builder builder) {
+ this.jobId = builder.jobId;
+ this.etag = builder.etag;
+ this.id = builder.id;
+ this.selfLink = builder.selfLink;
+ this.status = builder.status;
+ this.statistics = builder.statistics;
+ this.userEmail = builder.userEmail;
+ this.configuration = builder.configuration;
+ }
+
+ /**
+ * Returns the hash of the job resource.
+ */
+ public String etag() {
+ return etag;
+ }
+
+ /**
+ * Returns an opaque id for the job.
+ */
+ public String id() {
+ return id;
+ }
+
+ /**
+ * Returns the job identity.
+ */
+ public JobId jobId() {
+ return jobId;
+ }
+
+ /**
+ * Returns an URL that can be used to access the resource again. The returned URL can be used for
+ * GET requests.
+ */
+ public String selfLink() {
+ return selfLink;
+ }
+
+ /**
+ * Returns the status of this job. Examine this value when polling an asynchronous job to see if
+ * the job is complete.
+ */
+ public JobStatus status() {
+ return status;
+ }
+
+ /**
+ * Returns information about the job, including starting time and ending time of the job.
+ */
+ @SuppressWarnings("unchecked")
+ public