From b5791773f3c46a79128c4bae6fbf6226074fa9ae Mon Sep 17 00:00:00 2001 From: Arie Date: Fri, 7 Nov 2014 15:44:20 -0800 Subject: [PATCH 001/771] Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000000..157f813e551a --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +git-demo +======== From d5ae1bf0f98567ea46b39abb7805094a7a054ac0 Mon Sep 17 00:00:00 2001 From: aozarov Date: Fri, 7 Nov 2014 15:59:08 -0800 Subject: [PATCH 002/771] initial --- .checkstyle | 7 ++++ .classpath | 36 +++++++++++++++++++ .gitignore | 1 + .project | 29 +++++++++++++++ .settings/org.eclipse.jdt.core.prefs | 13 +++++++ .settings/org.eclipse.m2e.core.prefs | 4 +++ pom.xml | 6 ++++ .../gcloud/datastore/DatastoreService.java | 5 +++ 8 files changed, 101 insertions(+) create mode 100644 .checkstyle create mode 100644 .classpath create mode 100644 .gitignore create mode 100644 .project create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 .settings/org.eclipse.m2e.core.prefs create mode 100644 pom.xml create mode 100644 src/main/java/com/google/gcloud/datastore/DatastoreService.java diff --git a/.checkstyle b/.checkstyle new file mode 100644 index 000000000000..5783bc0d77a1 --- /dev/null +++ b/.checkstyle @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.classpath b/.classpath new file mode 100644 index 000000000000..9fc2de7b0d03 --- /dev/null +++ b/.classpath @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..b83d22266ac8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/.project b/.project new file mode 100644 index 000000000000..c8b8fcbb843d --- /dev/null +++ b/.project @@ -0,0 +1,29 @@ + + + git-demo + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + net.sf.eclipsecs.core.CheckstyleBuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + net.sf.eclipsecs.core.CheckstyleNature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000000..60ec3468d009 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,13 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000000..f897a7f1cb23 --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000000..6898b1fa6b19 --- /dev/null +++ b/pom.xml @@ -0,0 +1,6 @@ + + 4.0.0 + com.ozarov.testing + git-demo + 0.0.1-SNAPSHOT + \ No newline at end of file diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java new file mode 100644 index 000000000000..549fa0c0366f --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -0,0 +1,5 @@ +package com.google.gcloud.datastore; + +public interface DatastoreService { + +} From 5115b31a5d1a0d4a56b0d222bd66a3035cbb7650 Mon Sep 17 00:00:00 2001 From: aozarov Date: Fri, 7 Nov 2014 16:06:18 -0800 Subject: [PATCH 003/771] add dummy method --- src/main/java/com/google/gcloud/datastore/DatastoreService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index 549fa0c0366f..0ebf1b682608 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -2,4 +2,6 @@ public interface DatastoreService { + String getSchema(); + } From c7bac98fa8c75285c711acc3fa6870b69580553b Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 12 Nov 2014 09:41:10 -0800 Subject: [PATCH 004/771] Adding service options --- pom.xml | 12 ++++++++++++ .../google/gcloud/datastore/DatastoreService.java | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6898b1fa6b19..bc96c067d258 100644 --- a/pom.xml +++ b/pom.xml @@ -3,4 +3,16 @@ com.ozarov.testing git-demo 0.0.1-SNAPSHOT + + + com.google.http-client + google-http-client + 1.18.0-rc + + + com.google.oauth-client + google-oauth-client + 1.18.0-rc + + \ No newline at end of file diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index 0ebf1b682608..2121d2daa0c1 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -2,6 +2,6 @@ public interface DatastoreService { - String getSchema(); + String get(String key) } From 25fb54375270663755aa43e99103943f6e93fd9b Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 12 Nov 2014 09:48:28 -0800 Subject: [PATCH 005/771] Adding service options and factory --- .../com/google/gcloud/ServiceOptions.java | 116 +++++++++++++++++ .../datastore/DatastoreServiceFactory.java | 7 ++ .../datastore/DatastoreServiceOptions.java | 118 ++++++++++++++++++ 3 files changed, 241 insertions(+) create mode 100644 src/main/java/com/google/gcloud/ServiceOptions.java create mode 100644 src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java create mode 100644 src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java new file mode 100644 index 000000000000..80224cd1ca3f --- /dev/null +++ b/src/main/java/com/google/gcloud/ServiceOptions.java @@ -0,0 +1,116 @@ +package com.google.gcloud; + +import java.util.Arrays; +import java.util.List; + +import com.google.api.client.auth.oauth2.Credential; +import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.http.HttpTransport; + +public class ServiceOptions { + + private final String host; + private static final String DEFAULT_HOST = "https://www.googleapis.com"; + private final HttpRequestInitializer initializer; + private final Credential credential; + private final HttpTransport transport; + + public static final List SCOPES = Arrays.asList( + "https://www.googleapis.com/auth/datastore", + "https://www.googleapis.com/auth/userinfo.email"); + + ServiceOptions(Builder b) { + this.dataset = b.dataset; + this.host = b.host != null ? b.host : DEFAULT_HOST; + this.initializer = b.initializer; + this.credential = b.credential; + this.transport = b.transport; + } + + /** + * Builder for {@link ServiceOptions}. + */ + protected static class Builder { + private String dataset; + private String host; + private HttpRequestInitializer initializer; + private Credential credential; + private HttpTransport transport; + + public Builder() { } + + public Builder(ServiceOptions options) { + this.dataset = options.dataset; + this.host = options.host; + this.initializer = options.initializer; + this.credential = options.credential; + this.transport = options.transport; + } + + public ServiceOptions build() { + return new ServiceOptions(this); + } + + /** + * Sets the dataset used to access the datastore. + */ + public Builder dataset(String newDataset) { + dataset = newDataset; + return this; + } + + /** + * Sets the host used to access the datastore. + */ + public Builder host(String newHost) { + host = newHost; + return this; + } + + /** + * Sets the (optional) initializer to run on HTTP requests to the API. + */ + public Builder initializer(HttpRequestInitializer newInitializer) { + initializer = newInitializer; + return this; + } + + /** + * Sets the Google APIs credentials used to access the API. + */ + public Builder credential(Credential newCredential) { + credential = newCredential; + return this; + } + + /** + * Sets the transport used to access the API. + */ + public Builder transport(HttpTransport transport) { + this.transport = transport; + return this; + } + } + + // === getters === + + public String getDataset() { + return dataset; + } + + public String getHost() { + return host; + } + + public HttpRequestInitializer getInitializer() { + return initializer; + } + + public Credential getCredential() { + return credential; + } + + public HttpTransport getTransport() { + return transport; + } +} \ No newline at end of file diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java new file mode 100644 index 000000000000..aca1075fae64 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java @@ -0,0 +1,7 @@ +package com.google.gcloud.datastore; + + +public interface DatastoreServiceFactory { + + DatastoreService getDatastoreService(DatastoreServiceOptions options); +} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java new file mode 100644 index 000000000000..981d90515196 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java @@ -0,0 +1,118 @@ +package com.google.gcloud.datastore; + +import java.util.Arrays; +import java.util.List; + +import com.google.api.client.auth.oauth2.Credential; +import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.http.HttpTransport; +import com.google.gcloud.ServiceOptions; + +public class DatastoreServiceOptions extends ServiceOptions { + private final String dataset; + + private static final String DEFAULT_HOST = "https://www.googleapis.com"; + + private final HttpRequestInitializer initializer; + + private final Credential credential; + private final HttpTransport transport; + public static final List SCOPES = Arrays.asList( + "https://www.googleapis.com/auth/datastore", + "https://www.googleapis.com/auth/userinfo.email"); + + DatastoreServiceOptions(Builder b) { + this.dataset = b.dataset; + this.host = b.host != null ? b.host : DEFAULT_HOST; + this.initializer = b.initializer; + this.credential = b.credential; + this.transport = b.transport; + } + + /** + * Builder for {@link DatastoreServiceOptions}. + */ + public static class Builder { + private String dataset; + private String host; + private HttpRequestInitializer initializer; + private Credential credential; + private HttpTransport transport; + + public Builder() { } + + public Builder(DatastoreServiceOptions options) { + this.dataset = options.dataset; + this.host = options.host; + this.initializer = options.initializer; + this.credential = options.credential; + this.transport = options.transport; + } + + public DatastoreServiceOptions build() { + return new DatastoreServiceOptions(this); + } + + /** + * Sets the dataset used to access the datastore. + */ + public Builder dataset(String newDataset) { + dataset = newDataset; + return this; + } + + /** + * Sets the host used to access the datastore. + */ + public Builder host(String newHost) { + host = newHost; + return this; + } + + /** + * Sets the (optional) initializer to run on HTTP requests to the API. + */ + public Builder initializer(HttpRequestInitializer newInitializer) { + initializer = newInitializer; + return this; + } + + /** + * Sets the Google APIs credentials used to access the API. + */ + public Builder credential(Credential newCredential) { + credential = newCredential; + return this; + } + + /** + * Sets the transport used to access the API. + */ + public Builder transport(HttpTransport transport) { + this.transport = transport; + return this; + } + } + + // === getters === + + public String getDataset() { + return dataset; + } + + public String getHost() { + return host; + } + + public HttpRequestInitializer getInitializer() { + return initializer; + } + + public Credential getCredential() { + return credential; + } + + public HttpTransport getTransport() { + return transport; + } +} \ No newline at end of file From 68d808ac1cd8a032cf28adf2077df92d87e1cd12 Mon Sep 17 00:00:00 2001 From: ozarov Date: Wed, 12 Nov 2014 17:01:22 -0800 Subject: [PATCH 006/771] work in progress --- .classpath | 20 +--- .project | 8 +- .settings/org.eclipse.jdt.core.prefs | 98 ++++++++++++++-- pom.xml | 5 + .../com/google/gcloud/ServiceOptions.java | 102 +++------------- .../gcloud/datastore/DatastoreService.java | 2 +- .../datastore/DatastoreServiceFactory.java | 6 +- .../datastore/DatastoreServiceOptions.java | 110 +++--------------- 8 files changed, 149 insertions(+), 202 deletions(-) diff --git a/.classpath b/.classpath index 9fc2de7b0d03..86bbd24e2c1d 100644 --- a/.classpath +++ b/.classpath @@ -6,31 +6,21 @@ - - - - - - - - - - - - - - - + + + + + diff --git a/.project b/.project index c8b8fcbb843d..47ba158d4646 100644 --- a/.project +++ b/.project @@ -10,13 +10,18 @@ + + net.sf.eclipsecs.core.CheckstyleBuilder + + + org.eclipse.m2e.core.maven2Builder - net.sf.eclipsecs.core.CheckstyleBuilder + edu.umd.cs.findbugs.plugin.eclipse.findbugsBuilder @@ -25,5 +30,6 @@ org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature net.sf.eclipsecs.core.CheckstyleNature + edu.umd.cs.findbugs.plugin.eclipse.findbugsNature diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 60ec3468d009..2ffb1f42da80 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,13 +1,95 @@ eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.5 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=error +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.5 +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error +org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=warning +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/pom.xml b/pom.xml index bc96c067d258..3e0d202f7677 100644 --- a/pom.xml +++ b/pom.xml @@ -14,5 +14,10 @@ google-oauth-client 1.18.0-rc + + com.google.guava + guava + RELEASE + \ No newline at end of file diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java index 80224cd1ca3f..a435dbefb071 100644 --- a/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/src/main/java/com/google/gcloud/ServiceOptions.java @@ -1,116 +1,50 @@ package com.google.gcloud; -import java.util.Arrays; -import java.util.List; - import com.google.api.client.auth.oauth2.Credential; -import com.google.api.client.http.HttpRequestInitializer; -import com.google.api.client.http.HttpTransport; +import com.google.common.base.MoreObjects; -public class ServiceOptions { +public abstract class ServiceOptions { - private final String host; private static final String DEFAULT_HOST = "https://www.googleapis.com"; - private final HttpRequestInitializer initializer; - private final Credential credential; - private final HttpTransport transport; - public static final List SCOPES = Arrays.asList( - "https://www.googleapis.com/auth/datastore", - "https://www.googleapis.com/auth/userinfo.email"); + private final String host; + private final Credential credential; - ServiceOptions(Builder b) { - this.dataset = b.dataset; - this.host = b.host != null ? b.host : DEFAULT_HOST; - this.initializer = b.initializer; - this.credential = b.credential; - this.transport = b.transport; + protected ServiceOptions(Builder builder) { + host = MoreObjects.firstNonNull(builder.host, DEFAULT_HOST); + credential = builder.credential; } - /** - * Builder for {@link ServiceOptions}. - */ - protected static class Builder { - private String dataset; + protected abstract static class Builder { + private String host; - private HttpRequestInitializer initializer; private Credential credential; - private HttpTransport transport; - public Builder() { } + public Builder() {} public Builder(ServiceOptions options) { - this.dataset = options.dataset; - this.host = options.host; - this.initializer = options.initializer; - this.credential = options.credential; - this.transport = options.transport; + host = options.host; + credential = options.credential; } - public ServiceOptions build() { - return new ServiceOptions(this); - } - - /** - * Sets the dataset used to access the datastore. - */ - public Builder dataset(String newDataset) { - dataset = newDataset; - return this; - } + protected abstract ServiceOptions build(); - /** - * Sets the host used to access the datastore. - */ - public Builder host(String newHost) { - host = newHost; + public Builder setHost(String host) { + this.host = host; return this; } - /** - * Sets the (optional) initializer to run on HTTP requests to the API. - */ - public Builder initializer(HttpRequestInitializer newInitializer) { - initializer = newInitializer; - return this; - } - - /** - * Sets the Google APIs credentials used to access the API. - */ - public Builder credential(Credential newCredential) { - credential = newCredential; - return this; - } - - /** - * Sets the transport used to access the API. - */ - public Builder transport(HttpTransport transport) { - this.transport = transport; + public Builder setCredential(Credential credential) { + this.credential = credential; return this; } } - // === getters === - - public String getDataset() { - return dataset; - } - public String getHost() { return host; } - public HttpRequestInitializer getInitializer() { - return initializer; - } - public Credential getCredential() { return credential; } - - public HttpTransport getTransport() { - return transport; - } -} \ No newline at end of file +} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index 2121d2daa0c1..0923f42c5b19 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -2,6 +2,6 @@ public interface DatastoreService { - String get(String key) + String get(String key); } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java index aca1075fae64..903e33df5bf9 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java @@ -1,7 +1,9 @@ package com.google.gcloud.datastore; -public interface DatastoreServiceFactory { +public class DatastoreServiceFactory { - DatastoreService getDatastoreService(DatastoreServiceOptions options); + public DatastoreService getDatastoreService(DatastoreServiceOptions options) { + return new DatastoreServiceImpl(options); + } } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java index 981d90515196..13315ea62e7a 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java @@ -1,118 +1,46 @@ package com.google.gcloud.datastore; -import java.util.Arrays; -import java.util.List; - -import com.google.api.client.auth.oauth2.Credential; -import com.google.api.client.http.HttpRequestInitializer; -import com.google.api.client.http.HttpTransport; +import com.google.common.collect.ImmutableList; import com.google.gcloud.ServiceOptions; -public class DatastoreServiceOptions extends ServiceOptions { - private final String dataset; +import java.util.List; - private static final String DEFAULT_HOST = "https://www.googleapis.com"; +public class DatastoreServiceOptions extends ServiceOptions { - private final HttpRequestInitializer initializer; + private static final String DATASTORE_SCOPE = "https://www.googleapis.com/auth/datastore"; + private static final String USERINFO_SCOPE = "https://www.googleapis.com/auth/userinfo.email"; + private static final List SCOPES = ImmutableList.of(DATASTORE_SCOPE, USERINFO_SCOPE); - private final Credential credential; - private final HttpTransport transport; - public static final List SCOPES = Arrays.asList( - "https://www.googleapis.com/auth/datastore", - "https://www.googleapis.com/auth/userinfo.email"); + private final String dataset; - DatastoreServiceOptions(Builder b) { - this.dataset = b.dataset; - this.host = b.host != null ? b.host : DEFAULT_HOST; - this.initializer = b.initializer; - this.credential = b.credential; - this.transport = b.transport; + DatastoreServiceOptions(Builder builder) { + super(builder); + dataset = builder.dataset; } - /** - * Builder for {@link DatastoreServiceOptions}. - */ - public static class Builder { + public static class Builder extends ServiceOptions.Builder { + private String dataset; - private String host; - private HttpRequestInitializer initializer; - private Credential credential; - private HttpTransport transport; - public Builder() { } + public Builder() {} public Builder(DatastoreServiceOptions options) { - this.dataset = options.dataset; - this.host = options.host; - this.initializer = options.initializer; - this.credential = options.credential; - this.transport = options.transport; + super(options); + dataset = options.dataset; } + @Override public DatastoreServiceOptions build() { return new DatastoreServiceOptions(this); } - /** - * Sets the dataset used to access the datastore. - */ - public Builder dataset(String newDataset) { - dataset = newDataset; - return this; - } - - /** - * Sets the host used to access the datastore. - */ - public Builder host(String newHost) { - host = newHost; - return this; - } - - /** - * Sets the (optional) initializer to run on HTTP requests to the API. - */ - public Builder initializer(HttpRequestInitializer newInitializer) { - initializer = newInitializer; - return this; - } - - /** - * Sets the Google APIs credentials used to access the API. - */ - public Builder credential(Credential newCredential) { - credential = newCredential; - return this; - } - - /** - * Sets the transport used to access the API. - */ - public Builder transport(HttpTransport transport) { - this.transport = transport; + public Builder setDataset(String dataset) { + this.dataset = dataset; return this; } } - // === getters === - public String getDataset() { return dataset; } - - public String getHost() { - return host; - } - - public HttpRequestInitializer getInitializer() { - return initializer; - } - - public Credential getCredential() { - return credential; - } - - public HttpTransport getTransport() { - return transport; - } -} \ No newline at end of file +} From ef72daa81c73f99e2156f5bfe8127591fc6358e9 Mon Sep 17 00:00:00 2001 From: ozarov Date: Wed, 12 Nov 2014 22:36:12 -0800 Subject: [PATCH 007/771] work in progress --- pom.xml | 7 ++ .../com/google/gcloud/ServiceOptions.java | 75 ++++++++++++++++--- .../datastore/DatastoreServiceImpl.java | 17 +++++ .../datastore/DatastoreServiceOptions.java | 6 +- 4 files changed, 92 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java diff --git a/pom.xml b/pom.xml index 3e0d202f7677..318739824c67 100644 --- a/pom.xml +++ b/pom.xml @@ -19,5 +19,12 @@ guava RELEASE + + com.google.apis + + google-api-services-datastore-protobuf + + v1beta2-rev1-2.1.0 + \ No newline at end of file diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java index a435dbefb071..325daf08ce37 100644 --- a/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/src/main/java/com/google/gcloud/ServiceOptions.java @@ -1,30 +1,60 @@ package com.google.gcloud; -import com.google.api.client.auth.oauth2.Credential; + +import com.google.api.client.googleapis.compute.ComputeCredential; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.jackson.JacksonFactory; import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.PrivateKey; +import java.util.Set; public abstract class ServiceOptions { private static final String DEFAULT_HOST = "https://www.googleapis.com"; private final String host; - private final Credential credential; + private final HttpTransport httpTransport; + private final HttpRequestInitializer httpRequestInitializer; protected ServiceOptions(Builder builder) { host = MoreObjects.firstNonNull(builder.host, DEFAULT_HOST); - credential = builder.credential; + httpTransport = MoreObjects.firstNonNull(builder.httpTransport, getDefaultHttpTransport()); + httpRequestInitializer = builder.httpRequestInitializer; + } + + private static HttpTransport getDefaultHttpTransport() { + try { + NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport(); + // Try to connect using Google Compute Engine service account credentials. + ComputeCredential credential = new ComputeCredential(transport, new JacksonFactory()); + // Force token refresh to detect if we are running on Google Compute Engine. + credential.refreshToken(); + return credential.getTransport(); + } catch (IOException | GeneralSecurityException e) { + return new NetHttpTransport(); + } } protected abstract static class Builder { private String host; - private Credential credential; + private HttpTransport httpTransport; + private HttpRequestInitializer httpRequestInitializer; + private PrivateKey privateKey; public Builder() {} - public Builder(ServiceOptions options) { + protected Builder(ServiceOptions options) { host = options.host; - credential = options.credential; + httpTransport = options.httpTransport; + httpRequestInitializer = options.httpRequestInitializer; } protected abstract ServiceOptions build(); @@ -34,17 +64,42 @@ public Builder setHost(String host) { return this; } - public Builder setCredential(Credential credential) { - this.credential = credential; + public Builder setHttpTransport(HttpTransport httpTransport) { + this.httpTransport = httpTransport; + return this; + } + + public Builder setHttpRequestInitializer(HttpRequestInitializer httpRequestInitializer) { + // TODO: replace HttpRequestInitializer with CrendentialProvider - 2 subclasses + // one that is set with HttpRequestInitializer (and another that is set + // with both private key and service account) + // Also, consider instead of HttpRequestIntializer option to have "AppEngine" option + // which will use reflection to create HttpRequestInitializer + Preconditions.checkArgument( + privateKey == null, "Can't set both PrivateKey and HttpRequestInitializer"); + this.httpRequestInitializer = httpRequestInitializer; + return this; + } + + public Builder setPrivateKey(PrivateKey privateKey) { + Preconditions.checkArgument( + httpRequestInitializer == null, "Can't set both PrivateKey and HttpRequestInitializer"); + this.privateKey = privateKey; return this; } } + protected abstract Set getScopes(); + public String getHost() { return host; } - public Credential getCredential() { - return credential; + public HttpTransport getHttpTransport() { + return httpTransport; + } + + public HttpRequestInitializer getHttpRequestInitializer() { + return httpRequestInitializer; } } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java new file mode 100644 index 000000000000..4793436fd6f0 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -0,0 +1,17 @@ +package com.google.gcloud.datastore; + +final class DatastoreServiceImpl implements DatastoreService { + + private final DatastoreServiceOptions options; + + DatastoreServiceImpl(DatastoreServiceOptions options) { + this.options = options; + } + + @Override + public String get(String key) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java index 13315ea62e7a..065918d69ac3 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java @@ -1,15 +1,15 @@ package com.google.gcloud.datastore; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.gcloud.ServiceOptions; -import java.util.List; +import java.util.Set; public class DatastoreServiceOptions extends ServiceOptions { private static final String DATASTORE_SCOPE = "https://www.googleapis.com/auth/datastore"; private static final String USERINFO_SCOPE = "https://www.googleapis.com/auth/userinfo.email"; - private static final List SCOPES = ImmutableList.of(DATASTORE_SCOPE, USERINFO_SCOPE); + private static final Set SCOPES = ImmutableSet.of(DATASTORE_SCOPE, USERINFO_SCOPE); private final String dataset; From 22aa45fb27d68d0a035835a0e07a00784697f397 Mon Sep 17 00:00:00 2001 From: aozarov Date: Thu, 13 Nov 2014 12:59:51 -0800 Subject: [PATCH 008/771] dummy --- .../de.loskutov.anyedit.AnyEditTools.prefs | 18 ++++++++++++++++++ .settings/org.eclipse.jdt.core.prefs | 12 +++--------- 2 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 .settings/de.loskutov.anyedit.AnyEditTools.prefs diff --git a/.settings/de.loskutov.anyedit.AnyEditTools.prefs b/.settings/de.loskutov.anyedit.AnyEditTools.prefs new file mode 100644 index 000000000000..c9ca2a972d8a --- /dev/null +++ b/.settings/de.loskutov.anyedit.AnyEditTools.prefs @@ -0,0 +1,18 @@ +activeContentFilterList=*.makefile,makefile,*.Makefile,Makefile,Makefile.*,*.mk,MANIFEST.MF,.project +addNewLine=true +convertActionOnSaave=AnyEdit.CnvrtTabToSpaces +eclipse.preferences.version=1 +fixLineDelimiters=false +ignoreBlankLinesWhenTrimming=false +inActiveContentFilterList= +javaTabWidthForJava=true +org.eclipse.jdt.ui.editor.tab.width=2 +projectPropsEnabled=false +removeTrailingSpaces=true +replaceAllSpaces=false +replaceAllTabs=false +saveAndAddLine=false +saveAndConvert=false +saveAndFixLineDelimiters=false +saveAndTrim=true +useModulo4Tabs=false diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 60ec3468d009..f42de363afaa 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,13 +1,7 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.5 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.5 +org.eclipse.jdt.core.compiler.source=1.7 From 468d80ae3cf199683c64ae16053f890178c8c1bf Mon Sep 17 00:00:00 2001 From: aozarov Date: Thu, 13 Nov 2014 13:00:07 -0800 Subject: [PATCH 009/771] dummy --- .classpath | 10 +++++----- .project | 6 ++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.classpath b/.classpath index 9fc2de7b0d03..e177ae03a45e 100644 --- a/.classpath +++ b/.classpath @@ -1,5 +1,10 @@ + + + + + @@ -22,11 +27,6 @@ - - - - - diff --git a/.project b/.project index c8b8fcbb843d..9a5588b19d87 100644 --- a/.project +++ b/.project @@ -20,10 +20,16 @@ + + edu.umd.cs.findbugs.plugin.eclipse.findbugsBuilder + + + org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature net.sf.eclipsecs.core.CheckstyleNature + edu.umd.cs.findbugs.plugin.eclipse.findbugsNature From 40380c5c9e66fbebc9162a153c0b26f8ae9a7006 Mon Sep 17 00:00:00 2001 From: aozarov Date: Fri, 14 Nov 2014 18:01:43 -0800 Subject: [PATCH 010/771] work in progress --- pom.xml | 5 ++ .../java/com/google/gcloud/AuthConfig.java | 57 +++++++++++++ .../com/google/gcloud/ServiceOptions.java | 83 ++++++++++++------- .../datastore/DatastoreServiceOptions.java | 5 ++ 4 files changed, 119 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/google/gcloud/AuthConfig.java diff --git a/pom.xml b/pom.xml index 318739824c67..534629a90885 100644 --- a/pom.xml +++ b/pom.xml @@ -26,5 +26,10 @@ v1beta2-rev1-2.1.0 + + com.google.api-client + google-api-client-appengine + 1.18.0-rc + \ No newline at end of file diff --git a/src/main/java/com/google/gcloud/AuthConfig.java b/src/main/java/com/google/gcloud/AuthConfig.java new file mode 100644 index 000000000000..2bf34ae29ef8 --- /dev/null +++ b/src/main/java/com/google/gcloud/AuthConfig.java @@ -0,0 +1,57 @@ +package com.google.gcloud; + +import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; +import com.google.api.client.googleapis.extensions.appengine.auth.oauth2.AppIdentityCredential; +import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.json.jackson.JacksonFactory; + +import java.security.PrivateKey; +import java.util.Set; + +public abstract class AuthConfig { + + protected abstract HttpRequestInitializer getHttpRequestInitializer( + HttpTransport transport, Set scopes); + + + public static AuthConfig createForAppEngine() { + return new AppEngineAuthConfig(); + } + + public static AuthConfig createForAccount(String account, PrivateKey privateKey) { + return new ServiceAccountAuthConfig(account, privateKey); + } + + private static class AppEngineAuthConfig extends AuthConfig { + + @Override + protected HttpRequestInitializer getHttpRequestInitializer( + HttpTransport transport, Set scopes) { + return new AppIdentityCredential(scopes); + } + } + + private static class ServiceAccountAuthConfig extends AuthConfig { + + private final String account; + private final PrivateKey privateKey; + + public ServiceAccountAuthConfig(String account, PrivateKey privateKey) { + this.account = account; + this.privateKey = privateKey; + } + + @Override + protected HttpRequestInitializer getHttpRequestInitializer( + HttpTransport transport, Set scopes) { + return new GoogleCredential.Builder() + .setTransport(transport) + .setJsonFactory(new JacksonFactory()) + .setServiceAccountId(account) + .setServiceAccountScopes(scopes) + .setServiceAccountPrivateKey(privateKey) + .build(); + } + } +} diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java index 325daf08ce37..c924bef6be3c 100644 --- a/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/src/main/java/com/google/gcloud/ServiceOptions.java @@ -1,6 +1,7 @@ package com.google.gcloud; +import com.google.api.client.extensions.appengine.http.UrlFetchTransport; import com.google.api.client.googleapis.compute.ComputeCredential; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.HttpRequestInitializer; @@ -8,11 +9,9 @@ import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson.JacksonFactory; import com.google.common.base.MoreObjects; -import com.google.common.base.Preconditions; import java.io.IOException; import java.security.GeneralSecurityException; -import java.security.PrivateKey; import java.util.Set; public abstract class ServiceOptions { @@ -21,40 +20,76 @@ public abstract class ServiceOptions { private final String host; private final HttpTransport httpTransport; - private final HttpRequestInitializer httpRequestInitializer; + private final AuthConfig authConfig; protected ServiceOptions(Builder builder) { host = MoreObjects.firstNonNull(builder.host, DEFAULT_HOST); httpTransport = MoreObjects.firstNonNull(builder.httpTransport, getDefaultHttpTransport()); - httpRequestInitializer = builder.httpRequestInitializer; + authConfig = MoreObjects.firstNonNull(builder.authConfig, getDefaultAuthConfig()); } private static HttpTransport getDefaultHttpTransport() { + // Consider App Engine + if (System.getProperty("com.google.appengine.application.id") != null) { + try { + return new UrlFetchTransport(); + } catch (Exception ignore) { + // Maybe not on App Engine + } + } + // Consider Compute try { - NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport(); - // Try to connect using Google Compute Engine service account credentials. - ComputeCredential credential = new ComputeCredential(transport, new JacksonFactory()); - // Force token refresh to detect if we are running on Google Compute Engine. - credential.refreshToken(); - return credential.getTransport(); + return getComputeCredential().getTransport(); } catch (IOException | GeneralSecurityException e) { return new NetHttpTransport(); } } + private static AuthConfig getDefaultAuthConfig() { + // Consider App Engine + if (System.getProperty("com.google.appengine.application.id") != null) { + try { + return AuthConfig.createForAppEngine(); + } catch (Exception ignore) { + // Maybe not on App Engine + } + } + // Consider Compute + try { + final ComputeCredential cred = getComputeCredential(); + return new AuthConfig() { + @Override protected HttpRequestInitializer getHttpRequestInitializer( + HttpTransport transport, Set scopes) { + return cred; + } + }; + } catch (IOException | GeneralSecurityException e) { + return AuthConfig.createForAccount(null, null); + } + } + + private static ComputeCredential getComputeCredential() + throws IOException, GeneralSecurityException { + NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport(); + // Try to connect using Google Compute Engine service account credentials. + ComputeCredential credential = new ComputeCredential(transport, new JacksonFactory()); + // Force token refresh to detect if we are running on Google Compute Engine. + credential.refreshToken(); + return credential; + } + protected abstract static class Builder { private String host; private HttpTransport httpTransport; - private HttpRequestInitializer httpRequestInitializer; - private PrivateKey privateKey; + private AuthConfig authConfig; public Builder() {} protected Builder(ServiceOptions options) { host = options.host; httpTransport = options.httpTransport; - httpRequestInitializer = options.httpRequestInitializer; + authConfig = options.authConfig; } protected abstract ServiceOptions build(); @@ -69,22 +104,8 @@ public Builder setHttpTransport(HttpTransport httpTransport) { return this; } - public Builder setHttpRequestInitializer(HttpRequestInitializer httpRequestInitializer) { - // TODO: replace HttpRequestInitializer with CrendentialProvider - 2 subclasses - // one that is set with HttpRequestInitializer (and another that is set - // with both private key and service account) - // Also, consider instead of HttpRequestIntializer option to have "AppEngine" option - // which will use reflection to create HttpRequestInitializer - Preconditions.checkArgument( - privateKey == null, "Can't set both PrivateKey and HttpRequestInitializer"); - this.httpRequestInitializer = httpRequestInitializer; - return this; - } - - public Builder setPrivateKey(PrivateKey privateKey) { - Preconditions.checkArgument( - httpRequestInitializer == null, "Can't set both PrivateKey and HttpRequestInitializer"); - this.privateKey = privateKey; + public Builder setAuthConfig(AuthConfig authConfig) { + this.authConfig = authConfig; return this; } } @@ -99,7 +120,7 @@ public HttpTransport getHttpTransport() { return httpTransport; } - public HttpRequestInitializer getHttpRequestInitializer() { - return httpRequestInitializer; + public AuthConfig getAuthConfig() { + return authConfig; } } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java index 065918d69ac3..5ece5b39e378 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java @@ -43,4 +43,9 @@ public Builder setDataset(String dataset) { public String getDataset() { return dataset; } + + @Override + protected Set getScopes() { + return SCOPES; + } } From e2865eeea5e9095db886bc307e17abe42dc56e41 Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 18 Nov 2014 16:35:18 -0800 Subject: [PATCH 011/771] work in progress - adding datastore --- .settings/org.eclipse.jdt.core.prefs | 103 +--------- .../org.eclipse.ltk.core.refactoring.prefs | 2 + .../com/google/gcloud/ServiceOptions.java | 27 ++- .../google/gcloud/datastore/CompleteKey.java | 94 +++++++++ .../gcloud/datastore/DatastoreService.java | 15 +- .../datastore/DatastoreServiceImpl.java | 6 - .../datastore/DatastoreServiceOptions.java | 24 ++- .../java/com/google/gcloud/datastore/Key.java | 183 ++++++++++++++++++ .../google/gcloud/datastore/KeyMapValue.java | 24 +++ .../google/gcloud/datastore/NullValue.java | 31 +++ .../google/gcloud/datastore/StringValue.java | 47 +++++ .../com/google/gcloud/datastore/Value.java | 129 ++++++++++++ 12 files changed, 565 insertions(+), 120 deletions(-) create mode 100644 .settings/org.eclipse.ltk.core.refactoring.prefs create mode 100644 src/main/java/com/google/gcloud/datastore/CompleteKey.java create mode 100644 src/main/java/com/google/gcloud/datastore/Key.java create mode 100644 src/main/java/com/google/gcloud/datastore/KeyMapValue.java create mode 100644 src/main/java/com/google/gcloud/datastore/NullValue.java create mode 100644 src/main/java/com/google/gcloud/datastore/StringValue.java create mode 100644 src/main/java/com/google/gcloud/datastore/Value.java diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index afa74a52a006..0ba1de1e6e8a 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,101 +1,4 @@ +=\=\=\=\=\=\= +<<<<<<<=HEAD +>>>>>>>=ef72daa81c73f99e2156f5bfe8127591fc6358e9 eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled -org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore -org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault -org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable -org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 -org.eclipse.jdt.core.compiler.compliance=1.7 -<<<<<<< HEAD -======= -org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning ->>>>>>> ef72daa81c73f99e2156f5bfe8127591fc6358e9 -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.autoboxing=ignore -org.eclipse.jdt.core.compiler.problem.comparingIdentical=error -org.eclipse.jdt.core.compiler.problem.deadCode=warning -org.eclipse.jdt.core.compiler.problem.deprecation=warning -org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled -org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled -org.eclipse.jdt.core.compiler.problem.discouragedReference=warning -org.eclipse.jdt.core.compiler.problem.emptyStatement=warning -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -<<<<<<< HEAD -======= -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning -org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore -org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled -org.eclipse.jdt.core.compiler.problem.fieldHiding=warning -org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning -org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning -org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled -org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning -org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning -org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning -org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning -org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning -org.eclipse.jdt.core.compiler.problem.missingDefaultCase=warning -org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning -org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled -org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled -org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning -org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning -org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error -org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error -org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore -org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning -org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error -org.eclipse.jdt.core.compiler.problem.nullReference=warning -org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error -org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning -org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning -org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning -org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error -org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore -org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore -org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning -org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning -org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning -org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning -org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning -org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore -org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore -org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled -org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning -org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled -org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled -org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled -org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore -org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning -org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled -org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning -org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning -org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore -org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning -org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning -org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning -org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled -org.eclipse.jdt.core.compiler.problem.unusedImport=warning -org.eclipse.jdt.core.compiler.problem.unusedLabel=warning -org.eclipse.jdt.core.compiler.problem.unusedLocal=warning -org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning -org.eclipse.jdt.core.compiler.problem.unusedParameter=warning -org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled -org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning -org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning -org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning -org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning ->>>>>>> ef72daa81c73f99e2156f5bfe8127591fc6358e9 -org.eclipse.jdt.core.compiler.source=1.7 diff --git a/.settings/org.eclipse.ltk.core.refactoring.prefs b/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 000000000000..b196c64a3418 --- /dev/null +++ b/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java index c924bef6be3c..b3f5def4f9fc 100644 --- a/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/src/main/java/com/google/gcloud/ServiceOptions.java @@ -1,6 +1,8 @@ package com.google.gcloud; +import static com.google.common.base.MoreObjects.firstNonNull; + import com.google.api.client.extensions.appengine.http.UrlFetchTransport; import com.google.api.client.googleapis.compute.ComputeCredential; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; @@ -8,7 +10,6 @@ import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson.JacksonFactory; -import com.google.common.base.MoreObjects; import java.io.IOException; import java.security.GeneralSecurityException; @@ -23,14 +24,14 @@ public abstract class ServiceOptions { private final AuthConfig authConfig; protected ServiceOptions(Builder builder) { - host = MoreObjects.firstNonNull(builder.host, DEFAULT_HOST); - httpTransport = MoreObjects.firstNonNull(builder.httpTransport, getDefaultHttpTransport()); - authConfig = MoreObjects.firstNonNull(builder.authConfig, getDefaultAuthConfig()); + host = firstNonNull(builder.host, DEFAULT_HOST); + httpTransport = firstNonNull(builder.httpTransport, getDefaultHttpTransport()); + authConfig = firstNonNull(builder.authConfig, getDefaultAuthConfig()); } private static HttpTransport getDefaultHttpTransport() { // Consider App Engine - if (System.getProperty("com.google.appengine.application.id") != null) { + if (getAppEngineAppId() != null) { try { return new UrlFetchTransport(); } catch (Exception ignore) { @@ -40,14 +41,15 @@ private static HttpTransport getDefaultHttpTransport() { // Consider Compute try { return getComputeCredential().getTransport(); - } catch (IOException | GeneralSecurityException e) { - return new NetHttpTransport(); + } catch (Exception e) { + // Maybe not on GCE } + return new NetHttpTransport(); } private static AuthConfig getDefaultAuthConfig() { // Consider App Engine - if (System.getProperty("com.google.appengine.application.id") != null) { + if (getAppEngineAppId() != null) { try { return AuthConfig.createForAppEngine(); } catch (Exception ignore) { @@ -63,9 +65,14 @@ private static AuthConfig getDefaultAuthConfig() { return cred; } }; - } catch (IOException | GeneralSecurityException e) { - return AuthConfig.createForAccount(null, null); + } catch (Exception ignore) { + // Maybe not on GCE } + return AuthConfig.createForAccount(null, null); + } + + protected static String getAppEngineAppId() { + return System.getProperty("com.google.appengine.application.id"); } private static ComputeCredential getComputeCredential() diff --git a/src/main/java/com/google/gcloud/datastore/CompleteKey.java b/src/main/java/com/google/gcloud/datastore/CompleteKey.java new file mode 100644 index 000000000000..1ee40971baee --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/CompleteKey.java @@ -0,0 +1,94 @@ +package com.google.gcloud.datastore; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.gcloud.datastore.Key.PathEntry; + +import java.util.List; + +/** + * A key that is guaranteed to be complete. + */ +public final class CompleteKey { + + private final Key key; + + public static class Builder { + + private final Key.Builder keyBuilder = new Key.Builder(); + + public Builder(String dataset) { + this(dataset, ""); + } + + public Builder(String dataset, String namespace) { + keyBuilder.setDataset(checkNotNull(dataset)); + keyBuilder.setNamespace(checkNotNull(namespace)); + } + + public Builder addChild(String kind, long id) { + keyBuilder.addChild(kind, id); + return this; + } + + public Builder addChild(String kind, String name) { + keyBuilder.addChild(kind, name); + return this; + } + + public CompleteKey build() { + return new CompleteKey(keyBuilder.build()); + } + } + + private CompleteKey(Key key) { + this.key = key; + } + + public String getDataset() { + return key.getDataset(); + } + + public String getNamespace() { + return key.getNamespace(); + } + + public List getPath() { + return key.getPath(); + } + + public String getKind() { + return key.getKind(); + } + + public Long getId() { + return key.getLeaf().getId(); + } + + public String getName() { + return key.getLeaf().getName(); + } + + public Object getNameOrId() { + PathEntry leaf = key.getLeaf(); + return leaf.hasId() ? leaf.getId() : leaf.getName(); + } + + @Override + public String toString() { + return key.toString(); + } + + @Override + public int hashCode() { + return key.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof CompleteKey)) { + return false; + } + return key.equals(((CompleteKey) other).key); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index 0923f42c5b19..c280bb30a42a 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -1,7 +1,20 @@ package com.google.gcloud.datastore; +import java.util.Map; + public interface DatastoreService { - String get(String key); + DatastoreServiceOptions getOptions(); + + CompleteKey put(Key key, Map values); + + Map get(CompleteKey key); + + void delete(CompleteKey... key); + + void allocateIds(Key... key); + + // query result item is a tuple of (key, value...) where values may be empty + //QueryResult runQuery(Query query); } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index 4793436fd6f0..5046c81969b9 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -8,10 +8,4 @@ final class DatastoreServiceImpl implements DatastoreService { this.options = options; } - @Override - public String get(String key) { - // TODO Auto-generated method stub - return null; - } - } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java index 5ece5b39e378..e9a3f9183c0e 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java @@ -1,21 +1,28 @@ package com.google.gcloud.datastore; +import static com.google.common.base.MoreObjects.firstNonNull; +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import com.google.gcloud.ServiceOptions; import java.util.Set; +import java.util.regex.Pattern; public class DatastoreServiceOptions extends ServiceOptions { private static final String DATASTORE_SCOPE = "https://www.googleapis.com/auth/datastore"; private static final String USERINFO_SCOPE = "https://www.googleapis.com/auth/userinfo.email"; private static final Set SCOPES = ImmutableSet.of(DATASTORE_SCOPE, USERINFO_SCOPE); - + private static final Pattern PATTERN = Pattern.compile( + "([a-z\\d\\-]{1,100}~)?([a-z\\d][a-z\\d\\-\\.]{0,99}\\:)?([a-z\\d][a-z\\d\\-]{0,99})"); private final String dataset; DatastoreServiceOptions(Builder builder) { super(builder); - dataset = builder.dataset; + dataset = firstNonNull(builder.dataset, getAppEngineAppId()); + checkArgument(dataset != null, "missing dataset"); } public static class Builder extends ServiceOptions.Builder { @@ -35,11 +42,22 @@ public DatastoreServiceOptions build() { } public Builder setDataset(String dataset) { - this.dataset = dataset; + this.dataset = validateDataset(dataset); return this; } } + static String validateDataset(String dataset) { + if (Strings.isNullOrEmpty(dataset)) { + throw new IllegalArgumentException("dataset can't be empty or null"); + } + if (!PATTERN.matcher(dataset).matches()) { + throw new IllegalArgumentException( + "dataset must match the following pattern: " + PATTERN.pattern()); + } + return dataset; + } + public String getDataset() { return dataset; } diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java new file mode 100644 index 000000000000..cfb662569021 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -0,0 +1,183 @@ +package com.google.gcloud.datastore; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static com.google.gcloud.datastore.DatastoreServiceOptions.validateDataset; + +import com.google.api.services.datastore.DatastoreV1.Key.PathElement; +import com.google.api.services.datastore.DatastoreV1.PartitionId; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; + +public final class Key { + + private final String dataset; + private final String namespace; + private final ImmutableList path; + + public static final class PathEntry { + + private final String kind; + private final Long id; + private final String name; + + PathEntry(String kind) { + this.kind = kind; + this.id = null; + this.name = null; + } + + PathEntry(String kind, long id) { + checkArgument(id != 0, "id must not be equal to zero"); + this.kind = kind; + this.id = id; + this.name = null; + } + + PathEntry(String kind, String name) { + checkArgument(Strings.isNullOrEmpty(name) , "name must not be empty or null"); + checkArgument(name.length() <= 500, "name must not exceed 500 characters"); + this.kind = kind; + this.name = name; + this.id = null; + } + + public String getKind() { + return kind; + } + + public boolean hasId() { + return id != null; + } + + public long getId() { + return id == null ? 0 : id; + } + + public boolean hasName() { + return name != null; + } + + public String getName() { + return name == null ? "" : name; + } + } + + public static class Builder { + + private String dataset; + private String namespace; + private ImmutableList.Builder path = ImmutableList.builder(); + + public Builder addChild(String kind, long id) { + path.add(new PathEntry(kind, id)); + return this; + } + + public Builder addChild(String kind, String name) { + path.add(new PathEntry(kind, name)); + return this; + } + + public Builder addChild(String kind) { + path.add(new PathEntry(kind)); + return this; + } + + public Builder setDataset(String dataset) { + this.dataset = dataset == null ? null : validateDataset(dataset); + return this; + } + + public Builder setNamespace(String namespace) { + this.namespace = namespace; + return this; + } + + public Key build() { + return new Key(dataset, namespace, path.build()); + } + } + + private Key(String dataset, String namespace, ImmutableList path) { + checkState(!path.isEmpty(), "path must not be empty"); + this.dataset = dataset; + this.namespace = namespace; + this.path = path; + } + + public String getDataset() { + return dataset; + } + + public String getNamespace() { + return namespace; + } + + public List getPath() { + return path; + } + + public String getKind() { + return getLeaf().getKind(); + } + + @Override + public String toString() { + com.google.api.services.datastore.DatastoreV1.Value.Builder builder = + com.google.api.services.datastore.DatastoreV1.Value.newBuilder(); + addToPb(builder); + return builder.build().toString(); + } + + @Override + public int hashCode() { + return Objects.hash(dataset, namespace, path); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof Key)) { + return false; + } + Key otherKey = (Key) other; + return Objects.equals(dataset, otherKey.dataset) + && Objects.equals(namespace, otherKey.namespace) + && Objects.equals(path, otherKey.path); + } + + PathEntry getLeaf() { + return path.get(path.size() - 1); + } + + void addToPb(com.google.api.services.datastore.DatastoreV1.Value.Builder builder) { + com.google.api.services.datastore.DatastoreV1.Key.Builder keyBuilder = + com.google.api.services.datastore.DatastoreV1.Key.newBuilder(); + com.google.api.services.datastore.DatastoreV1.PartitionId.Builder partitionBuilder = + PartitionId.newBuilder(); + if (dataset != null) { + partitionBuilder.setDatasetId(dataset); + } + if (namespace != null) { + partitionBuilder.setNamespace(namespace); + } + if (partitionBuilder.hasDatasetId() || partitionBuilder.hasNamespace()) { + keyBuilder.setPartitionId(partitionBuilder.build()); + } + for (PathEntry pathEntry : path) { + com.google.api.services.datastore.DatastoreV1.Key.PathElement.Builder pathElementBuilder = + PathElement.newBuilder(); + pathElementBuilder.setKind(pathEntry.kind); + if (pathEntry.id != null) { + pathElementBuilder.setId(pathEntry.id); + } else if (pathEntry.name != null) { + pathElementBuilder.setName(pathEntry.name); + } + keyBuilder.addPathElement(pathElementBuilder.build()); + } + builder.setKeyValue(keyBuilder.build()); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/KeyMapValue.java b/src/main/java/com/google/gcloud/datastore/KeyMapValue.java new file mode 100644 index 000000000000..6dcbea74fd97 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/KeyMapValue.java @@ -0,0 +1,24 @@ +package com.google.gcloud.datastore; + +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +public class KeyMapValue extends Value { + + private final Key key; + private final ImmutableMap values; + + KeyMapValue(boolean indexed) { + super(Type. + // TODO Auto-generated constructor stub + } + + public Key getKey() { + return key; + } + + public Map getValues() { + return values; + } +} diff --git a/src/main/java/com/google/gcloud/datastore/NullValue.java b/src/main/java/com/google/gcloud/datastore/NullValue.java new file mode 100644 index 000000000000..3c9fe0f0621e --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/NullValue.java @@ -0,0 +1,31 @@ +package com.google.gcloud.datastore; + +import com.google.api.services.datastore.DatastoreV1.Value.Builder; + +public final class NullValue extends Value { + + static final Provider PROVIDER = new Provider() { + @Override + NullValue get(com.google.api.services.datastore.DatastoreV1.Value proto, boolean indexed, + Integer meaning) { + return new NullValue(indexed, meaning); + } + }; + + public NullValue() { + this(true); + } + + public NullValue(boolean indexed) { + this(indexed, null); + } + + public NullValue(boolean indexed, Integer meaning) { + super(Type.NULL, indexed, meaning); + } + + @Override + protected void addValueToProto(Builder builder) { + // set nothing + } +} diff --git a/src/main/java/com/google/gcloud/datastore/StringValue.java b/src/main/java/com/google/gcloud/datastore/StringValue.java new file mode 100644 index 000000000000..827fe7f43be3 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/StringValue.java @@ -0,0 +1,47 @@ +package com.google.gcloud.datastore; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.api.services.datastore.DatastoreV1.Value.Builder; + +// TODO: add javadoc, find the right place to describe that null should only +// be represented by NullValue (so nulls are not allowed here). +public final class StringValue extends Value { + + private final String content; + + static final Provider PROVIDER = new Provider() { + @Override + StringValue get(com.google.api.services.datastore.DatastoreV1.Value proto, boolean indexed, + Integer meaning) { + return new StringValue(proto.getStringValue(), indexed, meaning); + } + }; + + public StringValue(String content) { + this(content, true); + } + + public StringValue(String content, boolean indexed) { + this(content, indexed, null); + } + + public StringValue(String content, boolean indexed, Integer meaning) { + super(Type.STRING, indexed, meaning); + this.content = checkNotNull(content); + // some validations: + if (indexed) { + checkArgument(content.length() <= 500, "Indexed string is limited to 500 characters"); + } + } + + public String getContent() { + return content; + } + + @Override + protected void addValueToProto(Builder builder) { + builder.setStringValue(content); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/Value.java b/src/main/java/com/google/gcloud/datastore/Value.java new file mode 100644 index 000000000000..f600d9e4aef6 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/Value.java @@ -0,0 +1,129 @@ +package com.google.gcloud.datastore; + +import static com.google.api.services.datastore.DatastoreV1.Value.STRING_VALUE_FIELD_NUMBER; +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.api.services.datastore.DatastoreV1.Value.Builder; +import com.google.protobuf.Descriptors.FieldDescriptor; + +import java.util.Objects; + +public abstract class Value { + + abstract static class Provider { + + final V get(com.google.api.services.datastore.DatastoreV1.Value proto) { + return get(proto, proto.getIndexed(), proto.hasMeaning() ? proto.getMeaning() : null); + } + + abstract V get(com.google.api.services.datastore.DatastoreV1.Value proto, boolean indexed, + Integer meaning); + } + + public enum Type { + NULL(NullValue.PROVIDER, 0), + STRING(StringValue.PROVIDER, STRING_VALUE_FIELD_NUMBER), + /* + TODO: implement + LONG(LongValue.class, INTEGER_VALUE_FIELD_NUMBER), + DOUBLE(DoubleValue.class, DOUBLE_VALUE_FIELD_NUMBER), + // TODO: make sure that getContent returns an immutable value or at least a copy + TIMESTAMP(TimestampValue.class, TIMESTAMP_MICROSECONDS_VALUE_FIELD_NUMBER), + BOOLEAN(BooleanValue.class, BOOLEAN_VALUE_FIELD_NUMBER), + BLOB(BlobValue.class, BLOB_VALUE_FIELD_NUMBER), + BLOB_KEY(BlobKeyValue.class, BLOB_KEY_VALUE_FIELD_NUMBER), + */ + // TODO: does not seem to be public... + // GEO_POINT(GeoPointValue.class, 8), + COMPLETE_KEY_VALUE(CompleteKeyValue.PROVIDER, KEY_VALUE_FIELD_NUMBER), + KEY_MAP_VALUE(KeyMapValue.class, ENTITY_VALUE_FIELD_NUMBER), + List_VALUE(ListValue.class, LIST_VALUE_FIELD_NUMBER); + + private final Provider provider; + private FieldDescriptor field; + + Type(Provider provider, int idx) { + this.provider = provider; + this.field = com.google.api.services.datastore.DatastoreV1.Value.getDescriptor() + .findFieldByNumber(idx); + } + + Provider getProvider() { + return provider; + } + + FieldDescriptor getDescriptor() { + return field; + } + } + + private final Type type; + private final boolean indexed; + private final Integer meaning; + + Value(Type type, boolean indexed, Integer meaning) { + this.type = type; + this.indexed = indexed; + this.meaning = meaning; + // some validations: + if (meaning != null) { + // TODO: consider supplying Ranges for allowed meaning and validating it here + // more specific validation could be done on the specific types themselves + // upon construction [e.g. integer with a meaning 13 should be in the range [0,100]] + if (indexed) { + checkArgument(meaning != 15 && meaning != 22, + "Indexed values should not have meaning with 15 or 22"); + } + } + } + + public final Type getType() { + return type; + } + + public final boolean isIndexed() { + return indexed; + } + + public final Integer getMeaning() { + return meaning; + } + + @Override + public int hashCode() { + return Objects.hash(type, indexed, meaning); + } + + @Override + public boolean equals(Object other) { + if (!getClass().isInstance(other)) { + return false; + } + + Value otherValue = (Value) other; + return Objects.equals(type, otherValue.type) + && Objects.equals(indexed, otherValue.indexed) + && Objects.equals(meaning, otherValue.meaning); + } + + final com.google.api.services.datastore.DatastoreV1.Value toProto() { + Builder builder = com.google.api.services.datastore.DatastoreV1.Value.newBuilder(); + builder.setIndexed(indexed); + if (meaning != null) { + builder.setMeaning(meaning); + } + addValueToProto(builder); + return builder.build(); + } + + static Value fromProto(com.google.api.services.datastore.DatastoreV1.Value proto) { + for (Type type : Type.values()) { + if (proto.hasField(type.getDescriptor())) { + return type.getProvider().get(proto); + } + } + return NullValue.PROVIDER.get(proto); + } + + protected abstract void addValueToProto(Builder builder); +} From 5a47498a0a87bc3a6c1cee97afd5e6a981d7e6f2 Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 18 Nov 2014 16:36:01 -0800 Subject: [PATCH 012/771] Adding robusta config --- RobustaSettings.xml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 RobustaSettings.xml diff --git a/RobustaSettings.xml b/RobustaSettings.xml new file mode 100644 index 000000000000..e957a402fb2a --- /dev/null +++ b/RobustaSettings.xml @@ -0,0 +1,2 @@ + + From 9724180a570a4865fa16e599d9434bc9b74b1416 Mon Sep 17 00:00:00 2001 From: ozarov Date: Wed, 19 Nov 2014 17:58:26 -0800 Subject: [PATCH 013/771] rename Value to Property. Also, work in progress for making Property with a Builder and clone/toString/toBuilder (to allow modification)... --- .classpath | 5 - .../com.google.appengine.eclipse.core.prefs | 2 + .settings/edu.umd.cs.findbugs.core.prefs | 137 ++++++++++++++++++ .../google/gcloud/datastore/CompleteKey.java | 10 ++ .../gcloud/datastore/DatastoreService.java | 4 +- .../datastore/DatastoreServiceImpl.java | 26 ++++ .../google/gcloud/datastore/KeyMapValue.java | 25 +++- .../google/gcloud/datastore/NullProperty.java | 42 ++++++ .../google/gcloud/datastore/NullValue.java | 31 ---- .../datastore/{Value.java => Property.java} | 86 +++++++---- .../{StringValue.java => StringProperty.java} | 16 +- 11 files changed, 305 insertions(+), 79 deletions(-) create mode 100644 .settings/com.google.appengine.eclipse.core.prefs create mode 100644 .settings/edu.umd.cs.findbugs.core.prefs create mode 100644 src/main/java/com/google/gcloud/datastore/NullProperty.java delete mode 100644 src/main/java/com/google/gcloud/datastore/NullValue.java rename src/main/java/com/google/gcloud/datastore/{Value.java => Property.java} (57%) rename src/main/java/com/google/gcloud/datastore/{StringValue.java => StringProperty.java} (63%) diff --git a/.classpath b/.classpath index f1ba61b8c0a1..70f23326e0d2 100644 --- a/.classpath +++ b/.classpath @@ -17,11 +17,6 @@ - - - - - diff --git a/.settings/com.google.appengine.eclipse.core.prefs b/.settings/com.google.appengine.eclipse.core.prefs new file mode 100644 index 000000000000..82c36afe4e35 --- /dev/null +++ b/.settings/com.google.appengine.eclipse.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +filesCopiedToWebInfLib= diff --git a/.settings/edu.umd.cs.findbugs.core.prefs b/.settings/edu.umd.cs.findbugs.core.prefs new file mode 100644 index 000000000000..f2b5b2f9de9c --- /dev/null +++ b/.settings/edu.umd.cs.findbugs.core.prefs @@ -0,0 +1,137 @@ +#FindBugs User Preferences +#Wed Nov 19 14:11:27 PST 2014 +cloud_id=edu.umd.cs.findbugs.cloud.doNothingCloud +detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true +detectorAtomicityProblem=AtomicityProblem|true +detectorBadAppletConstructor=BadAppletConstructor|false +detectorBadResultSetAccess=BadResultSetAccess|true +detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true +detectorBadUseOfReturnValue=BadUseOfReturnValue|true +detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true +detectorBooleanReturnNull=BooleanReturnNull|true +detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false +detectorCheckExpectedWarnings=CheckExpectedWarnings|false +detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true +detectorCheckRelaxingNullnessAnnotation=CheckRelaxingNullnessAnnotation|true +detectorCheckTypeQualifiers=CheckTypeQualifiers|true +detectorCloneIdiom=CloneIdiom|true +detectorComparatorIdiom=ComparatorIdiom|true +detectorConfusedInheritance=ConfusedInheritance|true +detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true +detectorCrossSiteScripting=CrossSiteScripting|true +detectorDefaultEncodingDetector=DefaultEncodingDetector|true +detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true +detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true +detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true +detectorDontUseEnum=DontUseEnum|true +detectorDroppedException=DroppedException|true +detectorDumbMethodInvocations=DumbMethodInvocations|true +detectorDumbMethods=DumbMethods|true +detectorDuplicateBranches=DuplicateBranches|true +detectorEmptyZipFileEntry=EmptyZipFileEntry|false +detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true +detectorExplicitSerialization=ExplicitSerialization|true +detectorFinalizerNullsFields=FinalizerNullsFields|true +detectorFindBadCast2=FindBadCast2|true +detectorFindBadForLoop=FindBadForLoop|true +detectorFindCircularDependencies=FindCircularDependencies|false +detectorFindDeadLocalStores=FindDeadLocalStores|true +detectorFindDoubleCheck=FindDoubleCheck|true +detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true +detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true +detectorFindFinalizeInvocations=FindFinalizeInvocations|true +detectorFindFloatEquality=FindFloatEquality|true +detectorFindHEmismatch=FindHEmismatch|true +detectorFindInconsistentSync2=FindInconsistentSync2|true +detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true +detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true +detectorFindMaskedFields=FindMaskedFields|true +detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true +detectorFindNakedNotify=FindNakedNotify|true +detectorFindNonShortCircuit=FindNonShortCircuit|true +detectorFindNullDeref=FindNullDeref|true +detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true +detectorFindOpenStream=FindOpenStream|true +detectorFindPuzzlers=FindPuzzlers|true +detectorFindRefComparison=FindRefComparison|true +detectorFindReturnRef=FindReturnRef|true +detectorFindRoughConstants=FindRoughConstants|true +detectorFindRunInvocations=FindRunInvocations|true +detectorFindSelfComparison=FindSelfComparison|true +detectorFindSelfComparison2=FindSelfComparison2|true +detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true +detectorFindSpinLoop=FindSpinLoop|true +detectorFindSqlInjection=FindSqlInjection|true +detectorFindTwoLockWait=FindTwoLockWait|true +detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true +detectorFindUnconditionalWait=FindUnconditionalWait|true +detectorFindUninitializedGet=FindUninitializedGet|true +detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true +detectorFindUnreleasedLock=FindUnreleasedLock|true +detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true +detectorFindUnsyncGet=FindUnsyncGet|true +detectorFindUseOfNonSerializableValue=FindUseOfNonSerializableValue|true +detectorFindUselessControlFlow=FindUselessControlFlow|true +detectorFormatStringChecker=FormatStringChecker|true +detectorHugeSharedStringConstants=HugeSharedStringConstants|true +detectorIDivResultCastToDouble=IDivResultCastToDouble|true +detectorIncompatMask=IncompatMask|true +detectorInconsistentAnnotations=InconsistentAnnotations|true +detectorInefficientIndexOf=InefficientIndexOf|true +detectorInefficientMemberAccess=InefficientMemberAccess|false +detectorInefficientToArray=InefficientToArray|true +detectorInfiniteLoop=InfiniteLoop|true +detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true +detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true +detectorInitializationChain=InitializationChain|true +detectorInitializeNonnullFieldsInConstructor=InitializeNonnullFieldsInConstructor|true +detectorInstantiateStaticClass=InstantiateStaticClass|true +detectorIntCast2LongAsInstant=IntCast2LongAsInstant|true +detectorInvalidJUnitTest=InvalidJUnitTest|true +detectorIteratorIdioms=IteratorIdioms|true +detectorLazyInit=LazyInit|true +detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true +detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true +detectorMethodReturnCheck=MethodReturnCheck|true +detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true +detectorMutableLock=MutableLock|true +detectorMutableStaticFields=MutableStaticFields|true +detectorNaming=Naming|true +detectorNoteUnconditionalParamDerefs=NoteUnconditionalParamDerefs|true +detectorNumberConstructor=NumberConstructor|true +detectorOptionalReturnNull=OptionalReturnNull|true +detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true +detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true +detectorPublicSemaphores=PublicSemaphores|false +detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true +detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true +detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true +detectorRedundantInterfaces=RedundantInterfaces|true +detectorRepeatedConditionals=RepeatedConditionals|true +detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true +detectorSerializableIdiom=SerializableIdiom|true +detectorStartInConstructor=StartInConstructor|true +detectorStaticCalendarDetector=StaticCalendarDetector|true +detectorStringConcatenation=StringConcatenation|true +detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true +detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true +detectorSwitchFallthrough=SwitchFallthrough|true +detectorSynchronizationOnSharedBuiltinConstant=SynchronizationOnSharedBuiltinConstant|true +detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true +detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true +detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true +detectorURLProblems=URLProblems|true +detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true +detectorUnnecessaryMath=UnnecessaryMath|true +detectorUnreadFields=UnreadFields|true +detectorUselessSubclassMethod=UselessSubclassMethod|false +detectorVarArgsProblems=VarArgsProblems|true +detectorVolatileUsage=VolatileUsage|true +detectorWaitInLoop=WaitInLoop|true +detectorWrongMapIterator=WrongMapIterator|true +detectorXMLFactoryBypass=XMLFactoryBypass|true +detector_threshold=2 +effort=default +filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,EXPERIMENTAL,MT_CORRECTNESS,PERFORMANCE,STYLE|false|15 +filter_settings_neg=MALICIOUS_CODE,SECURITY,NOISE,I18N| +run_at_full_build=true diff --git a/src/main/java/com/google/gcloud/datastore/CompleteKey.java b/src/main/java/com/google/gcloud/datastore/CompleteKey.java index 1ee40971baee..fc66938f8920 100644 --- a/src/main/java/com/google/gcloud/datastore/CompleteKey.java +++ b/src/main/java/com/google/gcloud/datastore/CompleteKey.java @@ -74,6 +74,10 @@ public Object getNameOrId() { return leaf.hasId() ? leaf.getId() : leaf.getName(); } + public Key toKey() { + return key; + } + @Override public String toString() { return key.toString(); @@ -91,4 +95,10 @@ public boolean equals(Object other) { } return key.equals(((CompleteKey) other).key); } + + // TODO (here and in key - also consider for Properties): + // 1) cloneable + // 2) serializable (or externalizable) + // 3) toBuilder (and builder supports clear,...) + // 4) Builder accept builder } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index c280bb30a42a..44e517d10a07 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -6,9 +6,9 @@ public interface DatastoreService { DatastoreServiceOptions getOptions(); - CompleteKey put(Key key, Map values); + CompleteKey put(Key key, Map values); - Map get(CompleteKey key); + Map get(CompleteKey key); void delete(CompleteKey... key); diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index 5046c81969b9..2c55473ec71c 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -1,5 +1,7 @@ package com.google.gcloud.datastore; +import java.util.Map; + final class DatastoreServiceImpl implements DatastoreService { private final DatastoreServiceOptions options; @@ -8,4 +10,28 @@ final class DatastoreServiceImpl implements DatastoreService { this.options = options; } + public DatastoreServiceOptions getOptions() { + // TODO Auto-generated method stub + return null; + } + + public CompleteKey put(Key key, Map values) { + // TODO Auto-generated method stub + return null; + } + + public Map get(CompleteKey key) { + // TODO Auto-generated method stub + return null; + } + + public void delete(CompleteKey... key) { + // TODO Auto-generated method stub + + } + + public void allocateIds(Key... key) { + // TODO Auto-generated method stub + + } } diff --git a/src/main/java/com/google/gcloud/datastore/KeyMapValue.java b/src/main/java/com/google/gcloud/datastore/KeyMapValue.java index 6dcbea74fd97..7310bb49e341 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyMapValue.java +++ b/src/main/java/com/google/gcloud/datastore/KeyMapValue.java @@ -1,16 +1,28 @@ package com.google.gcloud.datastore; +import com.google.api.services.datastore.DatastoreV1.Value.Builder; import com.google.common.collect.ImmutableMap; import java.util.Map; -public class KeyMapValue extends Value { +public class KeyMapValue extends Property { private final Key key; - private final ImmutableMap values; + private final ImmutableMap values; + + static final Provider PROVIDER = new Provider() { + @Override + KeyMapValue get(com.google.api.services.datastore.DatastoreV1.Value proto, boolean indexed, + Integer meaning) { + // TODO: implement + return new KeyMapValue(indexed); + } + }; KeyMapValue(boolean indexed) { - super(Type. + super(Type.KEY_MAP_VALUE, indexed, 0); + key = null; + values = null; // TODO Auto-generated constructor stub } @@ -18,7 +30,12 @@ public Key getKey() { return key; } - public Map getValues() { + public Map getValues() { return values; } + + @Override + protected void addValueToProto(Builder builder) { + // TODO Auto-generated method stub + } } diff --git a/src/main/java/com/google/gcloud/datastore/NullProperty.java b/src/main/java/com/google/gcloud/datastore/NullProperty.java new file mode 100644 index 000000000000..32c865d572fa --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/NullProperty.java @@ -0,0 +1,42 @@ +package com.google.gcloud.datastore; + +import com.google.api.services.datastore.DatastoreV1.Value; + +public final class NullProperty extends Property { + + static final Provider PROVIDER = new Provider() { + @Override + NullProperty get(Value proto, boolean indexed, Integer meaning) { + return new NullProperty(indexed, meaning); + } + }; + + public static class Builder extends Property.Builder { + + public Builder() { + super(Type.NULL); + } + + @Override + public NullProperty build() { + return new NullProperty(); + } + } + + public NullProperty() { + this(true); + } + + public NullProperty(boolean indexed) { + this(indexed, null); + } + + public NullProperty(boolean indexed, Integer meaning) { + super(Type.NULL, indexed, meaning); + } + + @Override + protected void addValueToProto(Builder builder) { + // set nothing + } +} diff --git a/src/main/java/com/google/gcloud/datastore/NullValue.java b/src/main/java/com/google/gcloud/datastore/NullValue.java deleted file mode 100644 index 3c9fe0f0621e..000000000000 --- a/src/main/java/com/google/gcloud/datastore/NullValue.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.google.gcloud.datastore; - -import com.google.api.services.datastore.DatastoreV1.Value.Builder; - -public final class NullValue extends Value { - - static final Provider PROVIDER = new Provider() { - @Override - NullValue get(com.google.api.services.datastore.DatastoreV1.Value proto, boolean indexed, - Integer meaning) { - return new NullValue(indexed, meaning); - } - }; - - public NullValue() { - this(true); - } - - public NullValue(boolean indexed) { - this(indexed, null); - } - - public NullValue(boolean indexed, Integer meaning) { - super(Type.NULL, indexed, meaning); - } - - @Override - protected void addValueToProto(Builder builder) { - // set nothing - } -} diff --git a/src/main/java/com/google/gcloud/datastore/Value.java b/src/main/java/com/google/gcloud/datastore/Property.java similarity index 57% rename from src/main/java/com/google/gcloud/datastore/Value.java rename to src/main/java/com/google/gcloud/datastore/Property.java index f600d9e4aef6..2f158cd872ae 100644 --- a/src/main/java/com/google/gcloud/datastore/Value.java +++ b/src/main/java/com/google/gcloud/datastore/Property.java @@ -1,30 +1,40 @@ package com.google.gcloud.datastore; +import static com.google.api.services.datastore.DatastoreV1.Value.ENTITY_VALUE_FIELD_NUMBER; import static com.google.api.services.datastore.DatastoreV1.Value.STRING_VALUE_FIELD_NUMBER; import static com.google.common.base.Preconditions.checkArgument; +import com.google.api.services.datastore.DatastoreV1.Value; import com.google.api.services.datastore.DatastoreV1.Value.Builder; import com.google.protobuf.Descriptors.FieldDescriptor; import java.util.Objects; -public abstract class Value { +public abstract class Property { - abstract static class Provider { + private final Type type; + private final boolean indexed; + private final Integer meaning; - final V get(com.google.api.services.datastore.DatastoreV1.Value proto) { + abstract static class Provider { + + final V get(Value proto) { return get(proto, proto.getIndexed(), proto.hasMeaning() ? proto.getMeaning() : null); } - abstract V get(com.google.api.services.datastore.DatastoreV1.Value proto, boolean indexed, - Integer meaning); + abstract V get(Value proto, boolean indexed, Integer meaning); } public enum Type { - NULL(NullValue.PROVIDER, 0), - STRING(StringValue.PROVIDER, STRING_VALUE_FIELD_NUMBER), + + NULL(NullProperty.PROVIDER, 0), + STRING(StringProperty.PROVIDER, STRING_VALUE_FIELD_NUMBER), + // COMPLETE_KEY_VALUE(CompleteKeyValue.PROVIDER, KEY_VALUE_FIELD_NUMBER), + KEY_MAP_VALUE(KeyMapValue.PROVIDER, ENTITY_VALUE_FIELD_NUMBER); + // List_VALUE(ListValue.class, LIST_VALUE_FIELD_NUMBER); + /* - TODO: implement + TODO: Also implement LONG(LongValue.class, INTEGER_VALUE_FIELD_NUMBER), DOUBLE(DoubleValue.class, DOUBLE_VALUE_FIELD_NUMBER), // TODO: make sure that getContent returns an immutable value or at least a copy @@ -32,23 +42,18 @@ public enum Type { BOOLEAN(BooleanValue.class, BOOLEAN_VALUE_FIELD_NUMBER), BLOB(BlobValue.class, BLOB_VALUE_FIELD_NUMBER), BLOB_KEY(BlobKeyValue.class, BLOB_KEY_VALUE_FIELD_NUMBER), - */ - // TODO: does not seem to be public... - // GEO_POINT(GeoPointValue.class, 8), - COMPLETE_KEY_VALUE(CompleteKeyValue.PROVIDER, KEY_VALUE_FIELD_NUMBER), - KEY_MAP_VALUE(KeyMapValue.class, ENTITY_VALUE_FIELD_NUMBER), - List_VALUE(ListValue.class, LIST_VALUE_FIELD_NUMBER); - - private final Provider provider; + // GEO_POINT(GeoPointValue.class, 8) // Does not seem to be public yet... + */ + + private final Provider provider; private FieldDescriptor field; - Type(Provider provider, int idx) { + Type(Provider provider, int idx) { this.provider = provider; - this.field = com.google.api.services.datastore.DatastoreV1.Value.getDescriptor() - .findFieldByNumber(idx); + field = Value.getDescriptor().findFieldByNumber(idx); } - Provider getProvider() { + Provider getProvider() { return provider; } @@ -57,11 +62,28 @@ FieldDescriptor getDescriptor() { } } - private final Type type; - private final boolean indexed; - private final Integer meaning; + public static abstract class Builder

{ - Value(Type type, boolean indexed, Integer meaning) { + private final Type type; + private boolean indexed = true; + private Integer meaning; + + protected Builder(Type type) { + this.type = type; + } + + public void setIndexed(boolean indexed) { + this.indexed = indexed; + } + + public void setMeaning(Integer meaning) { + this.meaning = meaning; + } + + public abstract P build(); + } + + Property(Type type, boolean indexed, Integer meaning) { this.type = type; this.indexed = indexed; this.meaning = meaning; @@ -100,14 +122,14 @@ public boolean equals(Object other) { return false; } - Value otherValue = (Value) other; + Property otherValue = (Property) other; return Objects.equals(type, otherValue.type) && Objects.equals(indexed, otherValue.indexed) && Objects.equals(meaning, otherValue.meaning); } - final com.google.api.services.datastore.DatastoreV1.Value toProto() { - Builder builder = com.google.api.services.datastore.DatastoreV1.Value.newBuilder(); + final Value toProto() { + Builder builder = Value.newBuilder(); builder.setIndexed(indexed); if (meaning != null) { builder.setMeaning(meaning); @@ -116,13 +138,19 @@ final com.google.api.services.datastore.DatastoreV1.Value toProto() { return builder.build(); } - static Value fromProto(com.google.api.services.datastore.DatastoreV1.Value proto) { + // TODO: toString, clone?, serialize?, toBuilder, fromBuilder,... + + static Property fromProto(Value proto) { for (Type type : Type.values()) { if (proto.hasField(type.getDescriptor())) { return type.getProvider().get(proto); } } - return NullValue.PROVIDER.get(proto); + // change strategy to return RawProperty (package scope constructor) + // when no match instead of null. This could only be done + // when using the V1 API which added a NullValue type to distinct the cases + // and the use of oneof which generates an enum of all possible values. + return NullProperty.PROVIDER.get(proto); } protected abstract void addValueToProto(Builder builder); diff --git a/src/main/java/com/google/gcloud/datastore/StringValue.java b/src/main/java/com/google/gcloud/datastore/StringProperty.java similarity index 63% rename from src/main/java/com/google/gcloud/datastore/StringValue.java rename to src/main/java/com/google/gcloud/datastore/StringProperty.java index 827fe7f43be3..4ad8b90408a0 100644 --- a/src/main/java/com/google/gcloud/datastore/StringValue.java +++ b/src/main/java/com/google/gcloud/datastore/StringProperty.java @@ -3,31 +3,31 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.api.services.datastore.DatastoreV1.Value; import com.google.api.services.datastore.DatastoreV1.Value.Builder; // TODO: add javadoc, find the right place to describe that null should only // be represented by NullValue (so nulls are not allowed here). -public final class StringValue extends Value { +public final class StringProperty extends Property { private final String content; - static final Provider PROVIDER = new Provider() { + static final Provider PROVIDER = new Provider() { @Override - StringValue get(com.google.api.services.datastore.DatastoreV1.Value proto, boolean indexed, - Integer meaning) { - return new StringValue(proto.getStringValue(), indexed, meaning); + StringProperty get(Value proto, boolean indexed, Integer meaning) { + return new StringProperty(proto.getStringValue(), indexed, meaning); } }; - public StringValue(String content) { + public StringProperty(String content) { this(content, true); } - public StringValue(String content, boolean indexed) { + public StringProperty(String content, boolean indexed) { this(content, indexed, null); } - public StringValue(String content, boolean indexed, Integer meaning) { + public StringProperty(String content, boolean indexed, Integer meaning) { super(Type.STRING, indexed, meaning); this.content = checkNotNull(content); // some validations: From 36b70fdaddc131145bc96faeab0ad8958d9fcf2e Mon Sep 17 00:00:00 2001 From: ozarov Date: Wed, 19 Nov 2014 20:27:13 -0800 Subject: [PATCH 014/771] work in progress --- .../google/gcloud/datastore/NullProperty.java | 23 +++++++---- .../com/google/gcloud/datastore/Property.java | 41 +++++++++++-------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/NullProperty.java b/src/main/java/com/google/gcloud/datastore/NullProperty.java index 32c865d572fa..8ca9ed7efd80 100644 --- a/src/main/java/com/google/gcloud/datastore/NullProperty.java +++ b/src/main/java/com/google/gcloud/datastore/NullProperty.java @@ -2,7 +2,7 @@ import com.google.api.services.datastore.DatastoreV1.Value; -public final class NullProperty extends Property { +public final class NullProperty extends Property { static final Provider PROVIDER = new Provider() { @Override @@ -17,26 +17,31 @@ public Builder() { super(Type.NULL); } + public Builder(NullProperty property) { + super(property); + } + @Override public NullProperty build() { - return new NullProperty(); + return new NullProperty(this); } } public NullProperty() { - this(true); + this(new Builder()); } - public NullProperty(boolean indexed) { - this(indexed, null); + NullProperty(Builder builder) { + super(builder); } - public NullProperty(boolean indexed, Integer meaning) { - super(Type.NULL, indexed, meaning); + @Override + protected void addValueToProto(Value.Builder builder) { + // set nothing } @Override - protected void addValueToProto(Builder builder) { - // set nothing + public Property.Builder toBuilder() { + return new Builder(this); } } diff --git a/src/main/java/com/google/gcloud/datastore/Property.java b/src/main/java/com/google/gcloud/datastore/Property.java index 2f158cd872ae..8df283791098 100644 --- a/src/main/java/com/google/gcloud/datastore/Property.java +++ b/src/main/java/com/google/gcloud/datastore/Property.java @@ -5,24 +5,23 @@ import static com.google.common.base.Preconditions.checkArgument; import com.google.api.services.datastore.DatastoreV1.Value; -import com.google.api.services.datastore.DatastoreV1.Value.Builder; import com.google.protobuf.Descriptors.FieldDescriptor; import java.util.Objects; -public abstract class Property { +public abstract class Property

> { private final Type type; private final boolean indexed; private final Integer meaning; - abstract static class Provider { + abstract static class Provider

> { - final V get(Value proto) { + final P get(Value proto) { return get(proto, proto.getIndexed(), proto.hasMeaning() ? proto.getMeaning() : null); } - abstract V get(Value proto, boolean indexed, Integer meaning); + abstract P get(Value proto, boolean indexed, Integer meaning); } public enum Type { @@ -45,15 +44,15 @@ public enum Type { // GEO_POINT(GeoPointValue.class, 8) // Does not seem to be public yet... */ - private final Provider provider; + private final Provider> provider; private FieldDescriptor field; - Type(Provider provider, int idx) { + Type(Provider> provider, int idx) { this.provider = provider; field = Value.getDescriptor().findFieldByNumber(idx); } - Provider getProvider() { + Provider> getProvider() { return provider; } @@ -62,12 +61,18 @@ FieldDescriptor getDescriptor() { } } - public static abstract class Builder

{ + public abstract static class Builder

> { private final Type type; private boolean indexed = true; private Integer meaning; + protected Builder(Property

property) { + this.type = property.type; + this.indexed = property.indexed; + this.meaning = property.meaning; + } + protected Builder(Type type) { this.type = type; } @@ -83,10 +88,10 @@ public void setMeaning(Integer meaning) { public abstract P build(); } - Property(Type type, boolean indexed, Integer meaning) { - this.type = type; - this.indexed = indexed; - this.meaning = meaning; + Property(Builder

builder) { + this.type = builder.type; + this.indexed = builder.indexed; + this.meaning = builder.meaning; // some validations: if (meaning != null) { // TODO: consider supplying Ranges for allowed meaning and validating it here @@ -122,14 +127,14 @@ public boolean equals(Object other) { return false; } - Property otherValue = (Property) other; + Property otherValue = (Property) other; return Objects.equals(type, otherValue.type) && Objects.equals(indexed, otherValue.indexed) && Objects.equals(meaning, otherValue.meaning); } final Value toProto() { - Builder builder = Value.newBuilder(); + Value.Builder builder = Value.newBuilder(); builder.setIndexed(indexed); if (meaning != null) { builder.setMeaning(meaning); @@ -140,7 +145,7 @@ final Value toProto() { // TODO: toString, clone?, serialize?, toBuilder, fromBuilder,... - static Property fromProto(Value proto) { + static Property fromProto(Value proto) { for (Type type : Type.values()) { if (proto.hasField(type.getDescriptor())) { return type.getProvider().get(proto); @@ -153,5 +158,7 @@ static Property fromProto(Value proto) { return NullProperty.PROVIDER.get(proto); } - protected abstract void addValueToProto(Builder builder); + public abstract Builder

toBuilder(); + + protected abstract void addValueToProto(Value.Builder builder); } From b20b08414b611d0be6c156fb5cc454ec415e4035 Mon Sep 17 00:00:00 2001 From: aozarov Date: Thu, 20 Nov 2014 17:24:42 -0800 Subject: [PATCH 015/771] work in progress --- .../google/gcloud/datastore/KeyMapValue.java | 41 ---- .../gcloud/datastore/MapValueProperty.java | 63 ++++++ .../google/gcloud/datastore/NullProperty.java | 54 ++++-- .../com/google/gcloud/datastore/Property.java | 183 +++++++++++++----- .../gcloud/datastore/StringProperty.java | 73 ++++--- 5 files changed, 278 insertions(+), 136 deletions(-) delete mode 100644 src/main/java/com/google/gcloud/datastore/KeyMapValue.java create mode 100644 src/main/java/com/google/gcloud/datastore/MapValueProperty.java diff --git a/src/main/java/com/google/gcloud/datastore/KeyMapValue.java b/src/main/java/com/google/gcloud/datastore/KeyMapValue.java deleted file mode 100644 index 7310bb49e341..000000000000 --- a/src/main/java/com/google/gcloud/datastore/KeyMapValue.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.google.gcloud.datastore; - -import com.google.api.services.datastore.DatastoreV1.Value.Builder; -import com.google.common.collect.ImmutableMap; - -import java.util.Map; - -public class KeyMapValue extends Property { - - private final Key key; - private final ImmutableMap values; - - static final Provider PROVIDER = new Provider() { - @Override - KeyMapValue get(com.google.api.services.datastore.DatastoreV1.Value proto, boolean indexed, - Integer meaning) { - // TODO: implement - return new KeyMapValue(indexed); - } - }; - - KeyMapValue(boolean indexed) { - super(Type.KEY_MAP_VALUE, indexed, 0); - key = null; - values = null; - // TODO Auto-generated constructor stub - } - - public Key getKey() { - return key; - } - - public Map getValues() { - return values; - } - - @Override - protected void addValueToProto(Builder builder) { - // TODO Auto-generated method stub - } -} diff --git a/src/main/java/com/google/gcloud/datastore/MapValueProperty.java b/src/main/java/com/google/gcloud/datastore/MapValueProperty.java new file mode 100644 index 000000000000..877cd33b6a36 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/MapValueProperty.java @@ -0,0 +1,63 @@ +package com.google.gcloud.datastore; + +import com.google.api.services.datastore.DatastoreV1.Value; +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +public class MapValueProperty extends Property { + + private final Key key; + private final ImmutableMap values; + + static final Marshaller MARSHALLER = new Marshaller() { + @Override + MapValueProperty get(Value proto, boolean indexed, Integer meaning) { + // TODO: implement + return new MapValueProperty(indexed); + } + + @Override + public Builder newBuilder() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MapValueProperty fromProto(Value proto) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Value toProto(MapValueProperty property) { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getProtoFieldId() { + return ENTITY_VALUE_FIELD_NUMBER;; + } + }; + + MapValueProperty(boolean indexed) { + super(Type.KEY_MAP_VALUE, indexed, 0); + key = null; + values = null; + // TODO Auto-generated constructor stub + } + + public Key getKey() { + return key; + } + + public Map getValues() { + return values; + } + + @Override + protected void addValueToProto(Builder builder) { + // TODO Auto-generated method stub + } +} diff --git a/src/main/java/com/google/gcloud/datastore/NullProperty.java b/src/main/java/com/google/gcloud/datastore/NullProperty.java index 8ca9ed7efd80..570f85c22ad6 100644 --- a/src/main/java/com/google/gcloud/datastore/NullProperty.java +++ b/src/main/java/com/google/gcloud/datastore/NullProperty.java @@ -1,30 +1,56 @@ package com.google.gcloud.datastore; +import static com.google.common.base.Preconditions.checkArgument; + import com.google.api.services.datastore.DatastoreV1.Value; -public final class NullProperty extends Property { +public final class NullProperty extends Property { + + static final Marshaller MARSHALLER = + new BaseMarshaller() { + + @Override + public Builder newBuilder() { + return new Builder(); + } + + @Override + public int getProtoFieldId() { + return 0; + } + + @Override + protected void set(Value from, Builder to) { + // nothing to set + } - static final Provider PROVIDER = new Provider() { @Override - NullProperty get(Value proto, boolean indexed, Integer meaning) { - return new NullProperty(indexed, meaning); + protected void set(NullProperty from, Value.Builder to) { + // nothing to set } }; - public static class Builder extends Property.Builder { + public static final class Builder extends Property.Builder { public Builder() { super(Type.NULL); } - public Builder(NullProperty property) { - super(property); - } - @Override public NullProperty build() { return new NullProperty(this); } + + @Override + public Void validate(Void value) { + checkArgument(value == null, "Only null values are allowed"); + return null; + } + + @Override + protected Builder self() { + return this; + } } public NullProperty() { @@ -34,14 +60,4 @@ public NullProperty() { NullProperty(Builder builder) { super(builder); } - - @Override - protected void addValueToProto(Value.Builder builder) { - // set nothing - } - - @Override - public Property.Builder toBuilder() { - return new Builder(this); - } } diff --git a/src/main/java/com/google/gcloud/datastore/Property.java b/src/main/java/com/google/gcloud/datastore/Property.java index 8df283791098..ddaecca27579 100644 --- a/src/main/java/com/google/gcloud/datastore/Property.java +++ b/src/main/java/com/google/gcloud/datastore/Property.java @@ -1,7 +1,5 @@ package com.google.gcloud.datastore; -import static com.google.api.services.datastore.DatastoreV1.Value.ENTITY_VALUE_FIELD_NUMBER; -import static com.google.api.services.datastore.DatastoreV1.Value.STRING_VALUE_FIELD_NUMBER; import static com.google.common.base.Preconditions.checkArgument; import com.google.api.services.datastore.DatastoreV1.Value; @@ -9,27 +7,23 @@ import java.util.Objects; -public abstract class Property

> { +public abstract class Property, + B extends Property.Builder> { private final Type type; private final boolean indexed; private final Integer meaning; - - abstract static class Provider

> { - - final P get(Value proto) { - return get(proto, proto.getIndexed(), proto.hasMeaning() ? proto.getMeaning() : null); - } - - abstract P get(Value proto, boolean indexed, Integer meaning); - } + private final V value; public enum Type { - NULL(NullProperty.PROVIDER, 0), - STRING(StringProperty.PROVIDER, STRING_VALUE_FIELD_NUMBER), + NULL(NullProperty.MARSHALLER), + STRING(StringProperty.MARSHALLER); + // MapValue -> MapValueProperty + // ListValue -> ListValueProperty + // CompleteKey -> CompleteKeyProperty // COMPLETE_KEY_VALUE(CompleteKeyValue.PROVIDER, KEY_VALUE_FIELD_NUMBER), - KEY_MAP_VALUE(KeyMapValue.PROVIDER, ENTITY_VALUE_FIELD_NUMBER); + //KEY_MAP_VALUE(KeyMapValue.MARSHALLER); // List_VALUE(ListValue.class, LIST_VALUE_FIELD_NUMBER); /* @@ -44,16 +38,19 @@ public enum Type { // GEO_POINT(GeoPointValue.class, 8) // Does not seem to be public yet... */ - private final Provider> provider; + @SuppressWarnings("rawtypes") + private final Marshaller marshaller; private FieldDescriptor field; - Type(Provider> provider, int idx) { - this.provider = provider; - field = Value.getDescriptor().findFieldByNumber(idx); + , B extends Builder> + Type(Marshaller marshaller) { + this.marshaller = marshaller; + field = Value.getDescriptor().findFieldByNumber(marshaller.getProtoFieldId()); } - Provider> getProvider() { - return provider; + , B extends Builder> Marshaller + getMarshaller() { + return marshaller; } FieldDescriptor getDescriptor() { @@ -61,37 +58,109 @@ FieldDescriptor getDescriptor() { } } - public abstract static class Builder

> { + interface Marshaller, B extends Builder> { + + B newBuilder(); + + P fromProto(Value proto); + + Value toProto(P property); + + int getProtoFieldId(); + } + + abstract static class BaseMarshaller, B extends Builder> + implements Marshaller { + + @Override + public P fromProto(Value proto) { + B builder = newBuilder(); + builder.setIndexed(proto.getIndexed()); + if (proto.hasMeaning()) { + builder.setMeaning(proto.getMeaning()); + } + set(proto, builder); + return builder.build(); + } + + @Override + public final Value toProto(P property) { + Value.Builder builder = Value.newBuilder(); + builder.setIndexed(property.isIndexed()); + if (property.getMeaning() != null) { + builder.setMeaning(property.getMeaning()); + } + set(property, builder); + return builder.build(); + } + + protected abstract void set(Value from, B to); + + protected abstract void set(P from, Value.Builder to); + } + + public abstract static class Builder, B extends Builder> { private final Type type; private boolean indexed = true; private Integer meaning; - - protected Builder(Property

property) { - this.type = property.type; - this.indexed = property.indexed; - this.meaning = property.meaning; - } + private V value; protected Builder(Type type) { this.type = type; } - public void setIndexed(boolean indexed) { + public Type getType() { + return type; + } + + public B mergeFrom(P other) { + indexed = other.isIndexed(); + meaning = other.getMeaning(); + setValue(other.getValue()); + return self(); + } + + public boolean isIndexed() { + return indexed; + } + + public B setIndexed(boolean indexed) { this.indexed = indexed; + return self(); } - public void setMeaning(Integer meaning) { + public Integer getMeaning() { + return meaning; + } + + public B setMeaning(Integer meaning) { this.meaning = meaning; + return self(); + } + + public V getValue() { + return value; + } + + public B setValue(V value) { + this.value = validate(value); + return self(); } + protected V validate(V value) { + return value; + } + + protected abstract B self(); + public abstract P build(); } - Property(Builder

builder) { - this.type = builder.type; - this.indexed = builder.indexed; - this.meaning = builder.meaning; + Property(Builder builder) { + type = builder.getType(); + indexed = builder.isIndexed(); + meaning = builder.getMeaning(); // some validations: if (meaning != null) { // TODO: consider supplying Ranges for allowed meaning and validating it here @@ -102,6 +171,7 @@ public void setMeaning(Integer meaning) { "Indexed values should not have meaning with 15 or 22"); } } + value = builder.getValue(); } public final Type getType() { @@ -116,49 +186,56 @@ public final Integer getMeaning() { return meaning; } + public final V getValue() { + return value; + } + + @SuppressWarnings("unchecked") + public Builder toBuilder() { + @SuppressWarnings("rawtypes") + Builder builder = getType().getMarshaller().newBuilder(); + builder.mergeFrom(this); + return builder; + } + @Override public int hashCode() { - return Objects.hash(type, indexed, meaning); + return Objects.hash(getType(), isIndexed(), getMeaning()); } + @SuppressWarnings("unchecked") @Override public boolean equals(Object other) { if (!getClass().isInstance(other)) { return false; } - Property otherValue = (Property) other; - return Objects.equals(type, otherValue.type) - && Objects.equals(indexed, otherValue.indexed) - && Objects.equals(meaning, otherValue.meaning); + Property otherProperty = (Property) other; + return Objects.equals(type, otherProperty.getType()) + && Objects.equals(indexed, otherProperty.isIndexed()) + && Objects.equals(meaning, otherProperty.getMeaning()) + && Objects.equals(getValue(), otherProperty.getValue()); } + @SuppressWarnings({ "unchecked", "rawtypes" }) final Value toProto() { - Value.Builder builder = Value.newBuilder(); - builder.setIndexed(indexed); - if (meaning != null) { - builder.setMeaning(meaning); - } - addValueToProto(builder); - return builder.build(); + return ((Marshaller) getType().getMarshaller()).toProto(this); } // TODO: toString, clone?, serialize?, toBuilder, fromBuilder,... - static Property fromProto(Value proto) { + @SuppressWarnings("unchecked") + static , B extends Property.Builder> Property + fromProto(Value proto) { for (Type type : Type.values()) { if (proto.hasField(type.getDescriptor())) { - return type.getProvider().get(proto); + return (Property) type.getMarshaller().fromProto(proto); } } // change strategy to return RawProperty (package scope constructor) // when no match instead of null. This could only be done // when using the V1 API which added a NullValue type to distinct the cases // and the use of oneof which generates an enum of all possible values. - return NullProperty.PROVIDER.get(proto); + return (Property) NullProperty.MARSHALLER.fromProto(proto); } - - public abstract Builder

toBuilder(); - - protected abstract void addValueToProto(Value.Builder builder); } diff --git a/src/main/java/com/google/gcloud/datastore/StringProperty.java b/src/main/java/com/google/gcloud/datastore/StringProperty.java index 4ad8b90408a0..a012fdf6d2be 100644 --- a/src/main/java/com/google/gcloud/datastore/StringProperty.java +++ b/src/main/java/com/google/gcloud/datastore/StringProperty.java @@ -1,47 +1,74 @@ package com.google.gcloud.datastore; +import static com.google.api.services.datastore.DatastoreV1.Value.STRING_VALUE_FIELD_NUMBER; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import com.google.api.services.datastore.DatastoreV1.Value; -import com.google.api.services.datastore.DatastoreV1.Value.Builder; // TODO: add javadoc, find the right place to describe that null should only // be represented by NullValue (so nulls are not allowed here). -public final class StringProperty extends Property { +public final class StringProperty extends Property { - private final String content; + static final Marshaller MARSHALLER = + new BaseMarshaller() { - static final Provider PROVIDER = new Provider() { @Override - StringProperty get(Value proto, boolean indexed, Integer meaning) { - return new StringProperty(proto.getStringValue(), indexed, meaning); + public int getProtoFieldId() { + return STRING_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder() { + return new Builder(); + } + + @Override + protected void set(Value from, Builder to) { + to.setValue(from.getStringValue()); + } + + @Override + protected void set(StringProperty from, Value.Builder to) { + to.setStringValue(from.getValue()); } }; - public StringProperty(String content) { - this(content, true); - } + public static final class Builder extends Property.Builder { - public StringProperty(String content, boolean indexed) { - this(content, indexed, null); - } + Builder() { + this(""); + } + + public Builder(String value) { + super(Type.STRING); + setValue(value); + } - public StringProperty(String content, boolean indexed, Integer meaning) { - super(Type.STRING, indexed, meaning); - this.content = checkNotNull(content); - // some validations: - if (indexed) { - checkArgument(content.length() <= 500, "Indexed string is limited to 500 characters"); + @Override + public StringProperty build() { + if (isIndexed()) { + checkArgument(getValue().length() <= 500, "Indexed string is limited to 500 characters"); + } + return new StringProperty(this); + } + + @Override + public String validate(String value) { + return checkNotNull(value); + } + + @Override + protected Builder self() { + return this; } } - public String getContent() { - return content; + public StringProperty(String content) { + this(new Builder(content)); } - @Override - protected void addValueToProto(Builder builder) { - builder.setStringValue(content); + StringProperty(Builder builder) { + super(builder); } } From 688bce1a4dd9e08a3c37ea5ad73cfb03d9f56122 Mon Sep 17 00:00:00 2001 From: aozarov Date: Fri, 21 Nov 2014 16:02:28 -0800 Subject: [PATCH 016/771] work in progress --- .../java/com/google/gcloud/AuthConfig.java | 48 ++++++-- .../com/google/gcloud/ServiceOptions.java | 26 +--- .../java/com/google/gcloud/datastore/Key.java | 59 ++++++---- .../gcloud/datastore/MapValueProperty.java | 63 ---------- .../google/gcloud/datastore/NullProperty.java | 15 +-- .../com/google/gcloud/datastore/Property.java | 91 ++++++++------ .../gcloud/datastore/PropertyMapProperty.java | 111 ++++++++++++++++++ .../gcloud/datastore/StringProperty.java | 23 +--- 8 files changed, 253 insertions(+), 183 deletions(-) delete mode 100644 src/main/java/com/google/gcloud/datastore/MapValueProperty.java create mode 100644 src/main/java/com/google/gcloud/datastore/PropertyMapProperty.java diff --git a/src/main/java/com/google/gcloud/AuthConfig.java b/src/main/java/com/google/gcloud/AuthConfig.java index 2bf34ae29ef8..d6321e5488d7 100644 --- a/src/main/java/com/google/gcloud/AuthConfig.java +++ b/src/main/java/com/google/gcloud/AuthConfig.java @@ -1,28 +1,21 @@ package com.google.gcloud; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; +import com.google.api.client.googleapis.compute.ComputeCredential; import com.google.api.client.googleapis.extensions.appengine.auth.oauth2.AppIdentityCredential; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.HttpRequestInitializer; import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson.JacksonFactory; +import java.io.IOException; +import java.security.GeneralSecurityException; import java.security.PrivateKey; import java.util.Set; public abstract class AuthConfig { - protected abstract HttpRequestInitializer getHttpRequestInitializer( - HttpTransport transport, Set scopes); - - - public static AuthConfig createForAppEngine() { - return new AppEngineAuthConfig(); - } - - public static AuthConfig createForAccount(String account, PrivateKey privateKey) { - return new ServiceAccountAuthConfig(account, privateKey); - } - private static class AppEngineAuthConfig extends AuthConfig { @Override @@ -54,4 +47,35 @@ protected HttpRequestInitializer getHttpRequestInitializer( .build(); } } + + protected abstract HttpRequestInitializer getHttpRequestInitializer( + HttpTransport transport, Set scopes); + + + public static AuthConfig createForAppEngine() { + return new AppEngineAuthConfig(); + } + + public static AuthConfig createForComputeEngine() throws IOException, GeneralSecurityException { + final ComputeCredential cred = getComputeCredential(); + return new AuthConfig() { + @Override + protected HttpRequestInitializer getHttpRequestInitializer(HttpTransport ts, Set sc) { + return cred; + } + }; + } + + public static AuthConfig createForAccount(String account, PrivateKey privateKey) { + return new ServiceAccountAuthConfig(account, privateKey); + } + + static ComputeCredential getComputeCredential() throws IOException, GeneralSecurityException { + NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport(); + // Try to connect using Google Compute Engine service account credentials. + ComputeCredential credential = new ComputeCredential(transport, new JacksonFactory()); + // Force token refresh to detect if we are running on Google Compute Engine. + credential.refreshToken(); + return credential; + } } diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java index b3f5def4f9fc..974cfcddae1f 100644 --- a/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/src/main/java/com/google/gcloud/ServiceOptions.java @@ -4,15 +4,9 @@ import static com.google.common.base.MoreObjects.firstNonNull; import com.google.api.client.extensions.appengine.http.UrlFetchTransport; -import com.google.api.client.googleapis.compute.ComputeCredential; -import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; -import com.google.api.client.http.HttpRequestInitializer; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; -import com.google.api.client.json.jackson.JacksonFactory; -import java.io.IOException; -import java.security.GeneralSecurityException; import java.util.Set; public abstract class ServiceOptions { @@ -40,7 +34,7 @@ private static HttpTransport getDefaultHttpTransport() { } // Consider Compute try { - return getComputeCredential().getTransport(); + return AuthConfig.getComputeCredential().getTransport(); } catch (Exception e) { // Maybe not on GCE } @@ -58,13 +52,7 @@ private static AuthConfig getDefaultAuthConfig() { } // Consider Compute try { - final ComputeCredential cred = getComputeCredential(); - return new AuthConfig() { - @Override protected HttpRequestInitializer getHttpRequestInitializer( - HttpTransport transport, Set scopes) { - return cred; - } - }; + return AuthConfig.createForComputeEngine(); } catch (Exception ignore) { // Maybe not on GCE } @@ -75,16 +63,6 @@ protected static String getAppEngineAppId() { return System.getProperty("com.google.appengine.application.id"); } - private static ComputeCredential getComputeCredential() - throws IOException, GeneralSecurityException { - NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport(); - // Try to connect using Google Compute Engine service account credentials. - ComputeCredential credential = new ComputeCredential(transport, new JacksonFactory()); - // Force token refresh to detect if we are running on Google Compute Engine. - credential.refreshToken(); - return credential; - } - protected abstract static class Builder { private String host; diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index cfb662569021..0ad1f670c151 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -4,6 +4,7 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.gcloud.datastore.DatastoreServiceOptions.validateDataset; +import com.google.api.services.datastore.DatastoreV1; import com.google.api.services.datastore.DatastoreV1.Key.PathElement; import com.google.api.services.datastore.DatastoreV1.PartitionId; import com.google.common.base.Strings; @@ -127,10 +128,7 @@ public String getKind() { @Override public String toString() { - com.google.api.services.datastore.DatastoreV1.Value.Builder builder = - com.google.api.services.datastore.DatastoreV1.Value.newBuilder(); - addToPb(builder); - return builder.build().toString(); + return toPb().toString(); } @Override @@ -153,31 +151,52 @@ PathEntry getLeaf() { return path.get(path.size() - 1); } - void addToPb(com.google.api.services.datastore.DatastoreV1.Value.Builder builder) { - com.google.api.services.datastore.DatastoreV1.Key.Builder keyBuilder = - com.google.api.services.datastore.DatastoreV1.Key.newBuilder(); - com.google.api.services.datastore.DatastoreV1.PartitionId.Builder partitionBuilder = - PartitionId.newBuilder(); + static Key fromPb(DatastoreV1.Key keyPb) { + Builder builder = new Builder(); + if (keyPb.hasPartitionId()) { + PartitionId partitionIdPb = keyPb.getPartitionId(); + if (partitionIdPb.hasDatasetId()) { + builder.setDataset(partitionIdPb.getDatasetId()); + } + if (partitionIdPb.hasNamespace()) { + builder.setNamespace(partitionIdPb.getNamespace()); + } + } + for (PathElement pathElementPb : keyPb.getPathElementList()) { + String kind = pathElementPb.getKind(); + if (pathElementPb.hasId()) { + builder.addChild(kind, pathElementPb.getId()); + } else if (pathElementPb.hasName()) { + builder.addChild(kind, pathElementPb.getName()); + } else { + builder.addChild(kind); + } + } + return builder.build(); + } + + DatastoreV1.Key toPb() { + DatastoreV1.Key.Builder keyPb = DatastoreV1.Key.newBuilder(); + PartitionId.Builder partitionIdPb = PartitionId.newBuilder(); if (dataset != null) { - partitionBuilder.setDatasetId(dataset); + partitionIdPb.setDatasetId(dataset); } if (namespace != null) { - partitionBuilder.setNamespace(namespace); + partitionIdPb.setNamespace(namespace); } - if (partitionBuilder.hasDatasetId() || partitionBuilder.hasNamespace()) { - keyBuilder.setPartitionId(partitionBuilder.build()); + if (partitionIdPb.hasDatasetId() || partitionIdPb.hasNamespace()) { + keyPb.setPartitionId(partitionIdPb.build()); } for (PathEntry pathEntry : path) { - com.google.api.services.datastore.DatastoreV1.Key.PathElement.Builder pathElementBuilder = - PathElement.newBuilder(); - pathElementBuilder.setKind(pathEntry.kind); + PathElement.Builder pathElementPb = PathElement.newBuilder(); + pathElementPb.setKind(pathEntry.kind); if (pathEntry.id != null) { - pathElementBuilder.setId(pathEntry.id); + pathElementPb.setId(pathEntry.id); } else if (pathEntry.name != null) { - pathElementBuilder.setName(pathEntry.name); + pathElementPb.setName(pathEntry.name); } - keyBuilder.addPathElement(pathElementBuilder.build()); + keyPb.addPathElement(pathElementPb.build()); } - builder.setKeyValue(keyBuilder.build()); + return keyPb.build(); } } diff --git a/src/main/java/com/google/gcloud/datastore/MapValueProperty.java b/src/main/java/com/google/gcloud/datastore/MapValueProperty.java deleted file mode 100644 index 877cd33b6a36..000000000000 --- a/src/main/java/com/google/gcloud/datastore/MapValueProperty.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.google.gcloud.datastore; - -import com.google.api.services.datastore.DatastoreV1.Value; -import com.google.common.collect.ImmutableMap; - -import java.util.Map; - -public class MapValueProperty extends Property { - - private final Key key; - private final ImmutableMap values; - - static final Marshaller MARSHALLER = new Marshaller() { - @Override - MapValueProperty get(Value proto, boolean indexed, Integer meaning) { - // TODO: implement - return new MapValueProperty(indexed); - } - - @Override - public Builder newBuilder() { - // TODO Auto-generated method stub - return null; - } - - @Override - public MapValueProperty fromProto(Value proto) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Value toProto(MapValueProperty property) { - // TODO Auto-generated method stub - return null; - } - - @Override - public int getProtoFieldId() { - return ENTITY_VALUE_FIELD_NUMBER;; - } - }; - - MapValueProperty(boolean indexed) { - super(Type.KEY_MAP_VALUE, indexed, 0); - key = null; - values = null; - // TODO Auto-generated constructor stub - } - - public Key getKey() { - return key; - } - - public Map getValues() { - return values; - } - - @Override - protected void addValueToProto(Builder builder) { - // TODO Auto-generated method stub - } -} diff --git a/src/main/java/com/google/gcloud/datastore/NullProperty.java b/src/main/java/com/google/gcloud/datastore/NullProperty.java index 570f85c22ad6..0a9073995c6a 100644 --- a/src/main/java/com/google/gcloud/datastore/NullProperty.java +++ b/src/main/java/com/google/gcloud/datastore/NullProperty.java @@ -10,7 +10,7 @@ public final class NullProperty extends Property() { @Override - public Builder newBuilder() { + public Builder newBuilder(Value from) { return new Builder(); } @@ -20,17 +20,12 @@ public int getProtoFieldId() { } @Override - protected void set(Value from, Builder to) { - // nothing to set - } - - @Override - protected void set(NullProperty from, Value.Builder to) { + protected void setValueField(NullProperty from, Value.Builder to) { // nothing to set } }; - public static final class Builder extends Property.Builder { + public static final class Builder extends Property.BaseBuilder { public Builder() { super(Type.NULL); @@ -42,9 +37,9 @@ public NullProperty build() { } @Override - public Void validate(Void value) { + public Builder setValue(Void value) { checkArgument(value == null, "Only null values are allowed"); - return null; + return this; } @Override diff --git a/src/main/java/com/google/gcloud/datastore/Property.java b/src/main/java/com/google/gcloud/datastore/Property.java index ddaecca27579..c4a884ad9c39 100644 --- a/src/main/java/com/google/gcloud/datastore/Property.java +++ b/src/main/java/com/google/gcloud/datastore/Property.java @@ -1,14 +1,15 @@ package com.google.gcloud.datastore; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; import com.google.api.services.datastore.DatastoreV1.Value; import com.google.protobuf.Descriptors.FieldDescriptor; import java.util.Objects; -public abstract class Property, - B extends Property.Builder> { +public abstract class + Property, B extends Property.Builder> { private final Type type; private final boolean indexed; @@ -18,8 +19,8 @@ public abstract class Property, public enum Type { NULL(NullProperty.MARSHALLER), - STRING(StringProperty.MARSHALLER); - // MapValue -> MapValueProperty + STRING(StringProperty.MARSHALLER), + PROPERTY_MAP(PropertyMapProperty.MARSHALLER); // ListValue -> ListValueProperty // CompleteKey -> CompleteKeyProperty // COMPLETE_KEY_VALUE(CompleteKeyValue.PROVIDER, KEY_VALUE_FIELD_NUMBER), @@ -58,11 +59,30 @@ FieldDescriptor getDescriptor() { } } - interface Marshaller, B extends Builder> { + interface Builder, B extends Builder> { + + Type getType(); + + B mergeFrom(P other); + + boolean isIndexed(); - B newBuilder(); + B setIndexed(boolean indexed); - P fromProto(Value proto); + Integer getMeaning(); + + B setMeaning(Integer meaning); + + V getValue(); + + B setValue(V value); + + P build(); + } + + interface Marshaller, B extends Builder> { + + B fromProto(Value proto); Value toProto(P property); @@ -73,14 +93,13 @@ abstract static class BaseMarshaller, B extends B implements Marshaller { @Override - public P fromProto(Value proto) { - B builder = newBuilder(); + public final B fromProto(Value proto) { + B builder = newBuilder(proto); builder.setIndexed(proto.getIndexed()); if (proto.hasMeaning()) { builder.setMeaning(proto.getMeaning()); } - set(proto, builder); - return builder.build(); + return builder; } @Override @@ -90,30 +109,33 @@ public final Value toProto(P property) { if (property.getMeaning() != null) { builder.setMeaning(property.getMeaning()); } - set(property, builder); + setValueField(property, builder); return builder.build(); } - protected abstract void set(Value from, B to); + protected abstract B newBuilder(Value from); - protected abstract void set(P from, Value.Builder to); + protected abstract void setValueField(P from, Value.Builder to); } - public abstract static class Builder, B extends Builder> { + abstract static class BaseBuilder, B extends BaseBuilder> + implements Builder { private final Type type; private boolean indexed = true; private Integer meaning; private V value; - protected Builder(Type type) { + protected BaseBuilder(Type type) { this.type = type; } + @Override public Type getType() { return type; } + @Override public B mergeFrom(P other) { indexed = other.isIndexed(); meaning = other.getMeaning(); @@ -121,39 +143,42 @@ public B mergeFrom(P other) { return self(); } + @Override public boolean isIndexed() { return indexed; } + @Override public B setIndexed(boolean indexed) { this.indexed = indexed; return self(); } + @Override public Integer getMeaning() { return meaning; } + @Override public B setMeaning(Integer meaning) { this.meaning = meaning; return self(); } + @Override public V getValue() { return value; } + @Override public B setValue(V value) { - this.value = validate(value); + this.value = checkNotNull(value); return self(); } - protected V validate(V value) { - return value; - } - protected abstract B self(); + @Override public abstract P build(); } @@ -190,12 +215,9 @@ public final V getValue() { return value; } - @SuppressWarnings("unchecked") - public Builder toBuilder() { - @SuppressWarnings("rawtypes") - Builder builder = getType().getMarshaller().newBuilder(); - builder.mergeFrom(this); - return builder; + public final B toBuilder() { + Marshaller marshaller = getType().getMarshaller(); + return marshaller.fromProto(toPb()); } @Override @@ -217,25 +239,24 @@ public boolean equals(Object other) { && Objects.equals(getValue(), otherProperty.getValue()); } - @SuppressWarnings({ "unchecked", "rawtypes" }) - final Value toProto() { - return ((Marshaller) getType().getMarshaller()).toProto(this); + @SuppressWarnings("unchecked") + Value toPb() { + Marshaller marshaller = getType().getMarshaller(); + return marshaller.toProto((P) this); } - // TODO: toString, clone?, serialize?, toBuilder, fromBuilder,... - @SuppressWarnings("unchecked") static , B extends Property.Builder> Property - fromProto(Value proto) { + fromPb(Value proto) { for (Type type : Type.values()) { if (proto.hasField(type.getDescriptor())) { - return (Property) type.getMarshaller().fromProto(proto); + return (Property) type.getMarshaller().fromProto(proto).build(); } } // change strategy to return RawProperty (package scope constructor) // when no match instead of null. This could only be done // when using the V1 API which added a NullValue type to distinct the cases // and the use of oneof which generates an enum of all possible values. - return (Property) NullProperty.MARSHALLER.fromProto(proto); + return (Property) new NullProperty(); } } diff --git a/src/main/java/com/google/gcloud/datastore/PropertyMapProperty.java b/src/main/java/com/google/gcloud/datastore/PropertyMapProperty.java new file mode 100644 index 000000000000..a66aa57910be --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/PropertyMapProperty.java @@ -0,0 +1,111 @@ +package com.google.gcloud.datastore; + +import static com.google.api.client.util.Preconditions.checkNotNull; +import static com.google.api.services.datastore.DatastoreV1.Value.ENTITY_VALUE_FIELD_NUMBER; + +import com.google.api.services.datastore.DatastoreV1; +import com.google.api.services.datastore.DatastoreV1.Entity; +import com.google.api.services.datastore.DatastoreV1.Value; +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +public final class PropertyMapProperty extends + Property>, PropertyMapProperty, PropertyMapProperty.Builder> { + + private final Key key; + + static final Marshaller>, PropertyMapProperty, Builder> MARSHALLER = + new BaseMarshaller>, PropertyMapProperty, Builder>() { + + @Override + public int getProtoFieldId() { + return ENTITY_VALUE_FIELD_NUMBER; + } + + @Override + protected Builder newBuilder(Value from) { + Builder builder = new Builder(); + Entity entityPb = from.getEntityValue(); + if (entityPb.hasKey()) { + builder.setKey(Key.fromPb(entityPb.getKey())); + } + for (DatastoreV1.Property propertyPb : entityPb.getPropertyList()) { + builder.addProperty(propertyPb.getName(), Property.fromPb(propertyPb.getValue())); + } + return builder; + } + + @Override + protected void setValueField(PropertyMapProperty from, DatastoreV1.Value.Builder to) { + Entity.Builder entityPb = Entity.newBuilder(); + entityPb.setKey(from.getKey().toPb()); + for (Map.Entry> entry : from.getValue().entrySet()) { + Property property = entry.getValue(); + DatastoreV1.Property.Builder propertyPb = DatastoreV1.Property.newBuilder(); + propertyPb.setName(entry.getKey()); + propertyPb.setValue(property.toPb()); + } + } + }; + + public static final class Builder extends + Property.BaseBuilder>, PropertyMapProperty, Builder> { + + private Key key; + private ImmutableMap.Builder> mapBuilder = ImmutableMap.builder(); + + public Builder() { + super(Type.PROPERTY_MAP); + setIndexed(false); + } + + public Key getKey() { + return key; + } + + public Builder setKey(Key key) { + this.key = key; + return this; + } + + public Builder addProperty(String name, Property property) { + mapBuilder.put(name, property); + return this; + } + + @Override + public Builder setValue(Map> value) { + mapBuilder = ImmutableMap.>builder().putAll(checkNotNull(value)); + return this; + } + + @Override + public Map> getValue() { + return mapBuilder.build(); + } + + @Override + protected Builder self() { + return this; + } + + @Override + public PropertyMapProperty build() { + return new PropertyMapProperty(this); + } + } + + public PropertyMapProperty(Key key, Map> properties) { + this(new Builder().setValue(properties).setKey(key)); + } + + PropertyMapProperty(Builder builder) { + super(builder); + key = builder.key; + } + + public Key getKey() { + return key; + } +} diff --git a/src/main/java/com/google/gcloud/datastore/StringProperty.java b/src/main/java/com/google/gcloud/datastore/StringProperty.java index a012fdf6d2be..df83288969d1 100644 --- a/src/main/java/com/google/gcloud/datastore/StringProperty.java +++ b/src/main/java/com/google/gcloud/datastore/StringProperty.java @@ -2,7 +2,6 @@ import static com.google.api.services.datastore.DatastoreV1.Value.STRING_VALUE_FIELD_NUMBER; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; import com.google.api.services.datastore.DatastoreV1.Value; @@ -19,26 +18,17 @@ public int getProtoFieldId() { } @Override - public Builder newBuilder() { - return new Builder(); + protected Builder newBuilder(Value from) { + return new Builder(from.getStringValue()); } @Override - protected void set(Value from, Builder to) { - to.setValue(from.getStringValue()); - } - - @Override - protected void set(StringProperty from, Value.Builder to) { + protected void setValueField(StringProperty from, Value.Builder to) { to.setStringValue(from.getValue()); } }; - public static final class Builder extends Property.Builder { - - Builder() { - this(""); - } + public static final class Builder extends Property.BaseBuilder { public Builder(String value) { super(Type.STRING); @@ -53,11 +43,6 @@ public StringProperty build() { return new StringProperty(this); } - @Override - public String validate(String value) { - return checkNotNull(value); - } - @Override protected Builder self() { return this; From 1157e324b8311d9e6f0b9ff94abff117b97d3ebc Mon Sep 17 00:00:00 2001 From: aozarov Date: Mon, 24 Nov 2014 17:39:34 -0800 Subject: [PATCH 017/771] work-in-progress --- .../google/gcloud/datastore/CompleteKey.java | 104 ------ .../gcloud/datastore/DatastoreService.java | 72 +++- .../datastore/DatastoreServiceImpl.java | 25 +- .../gcloud/datastore/IncompleteKey.java | 313 ++++++++++++++++++ .../java/com/google/gcloud/datastore/Key.java | 195 +++-------- .../google/gcloud/datastore/KeyProperty.java | 55 +++ .../google/gcloud/datastore/NullProperty.java | 2 + .../com/google/gcloud/datastore/Property.java | 46 ++- .../datastore/PropertyListProperty.java | 87 +++++ .../gcloud/datastore/PropertyMapProperty.java | 16 +- .../gcloud/datastore/StringProperty.java | 4 +- 11 files changed, 607 insertions(+), 312 deletions(-) delete mode 100644 src/main/java/com/google/gcloud/datastore/CompleteKey.java create mode 100644 src/main/java/com/google/gcloud/datastore/IncompleteKey.java create mode 100644 src/main/java/com/google/gcloud/datastore/KeyProperty.java create mode 100644 src/main/java/com/google/gcloud/datastore/PropertyListProperty.java diff --git a/src/main/java/com/google/gcloud/datastore/CompleteKey.java b/src/main/java/com/google/gcloud/datastore/CompleteKey.java deleted file mode 100644 index fc66938f8920..000000000000 --- a/src/main/java/com/google/gcloud/datastore/CompleteKey.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.google.gcloud.datastore; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.gcloud.datastore.Key.PathEntry; - -import java.util.List; - -/** - * A key that is guaranteed to be complete. - */ -public final class CompleteKey { - - private final Key key; - - public static class Builder { - - private final Key.Builder keyBuilder = new Key.Builder(); - - public Builder(String dataset) { - this(dataset, ""); - } - - public Builder(String dataset, String namespace) { - keyBuilder.setDataset(checkNotNull(dataset)); - keyBuilder.setNamespace(checkNotNull(namespace)); - } - - public Builder addChild(String kind, long id) { - keyBuilder.addChild(kind, id); - return this; - } - - public Builder addChild(String kind, String name) { - keyBuilder.addChild(kind, name); - return this; - } - - public CompleteKey build() { - return new CompleteKey(keyBuilder.build()); - } - } - - private CompleteKey(Key key) { - this.key = key; - } - - public String getDataset() { - return key.getDataset(); - } - - public String getNamespace() { - return key.getNamespace(); - } - - public List getPath() { - return key.getPath(); - } - - public String getKind() { - return key.getKind(); - } - - public Long getId() { - return key.getLeaf().getId(); - } - - public String getName() { - return key.getLeaf().getName(); - } - - public Object getNameOrId() { - PathEntry leaf = key.getLeaf(); - return leaf.hasId() ? leaf.getId() : leaf.getName(); - } - - public Key toKey() { - return key; - } - - @Override - public String toString() { - return key.toString(); - } - - @Override - public int hashCode() { - return key.hashCode(); - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof CompleteKey)) { - return false; - } - return key.equals(((CompleteKey) other).key); - } - - // TODO (here and in key - also consider for Properties): - // 1) cloneable - // 2) serializable (or externalizable) - // 3) toBuilder (and builder supports clear,...) - // 4) Builder accept builder -} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index 44e517d10a07..b876b721df6b 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -1,20 +1,78 @@ package com.google.gcloud.datastore; +import java.util.Iterator; import java.util.Map; public interface DatastoreService { - DatastoreServiceOptions getOptions(); + interface DatastoreReader { + + Map> get(Key key); + + // return the result in the given order + Iterator>> get(Iterator key); + + // query result item is a tuple of (key, value...) where values may be empty + //QueryResult runQuery(Query query); + } + + + interface DatastoreWriter { + + Key add(IncompleteKey key, Map> values); + + void update(Key key , Map> values); + + Key put(IncompleteKey key, Map> values); + + void delete(Key key); + } + + + public interface Transaction extends DatastoreReader, DatastoreWriter { + + void commit(); - CompleteKey put(Key key, Map values); + void rollback(); + } + + public interface TransactionOptions { + + enum IsolationLevel { + SERIALIZABLE, SNAPSHOT; + } + + IsolationLevel getIsolationLevel(); + } + + public interface Batch extends DatastoreWriter { + + @Override + void add(Key key, Map> values); + + @Override + void update(Key key , Map> values); + + @Override + void put(Key key, Map> values); + + void submit(); + } + + public interface BatchOptions { + + } + + DatastoreServiceOptions getOptions(); - Map get(CompleteKey key); + Transaction newTransaction(TransactionOptions tsOptions); - void delete(CompleteKey... key); + Batch newBatch(); - void allocateIds(Key... key); + P - // query result item is a tuple of (key, value...) where values may be empty - //QueryResult runQuery(Query query); + Key allocateId(IncompleteKey key); + // results are returned in request order + Iterator allocateIds(Iterator key); } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index 2c55473ec71c..f324081edaad 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -1,6 +1,5 @@ package com.google.gcloud.datastore; -import java.util.Map; final class DatastoreServiceImpl implements DatastoreService { @@ -10,28 +9,8 @@ final class DatastoreServiceImpl implements DatastoreService { this.options = options; } + @Override public DatastoreServiceOptions getOptions() { - // TODO Auto-generated method stub - return null; - } - - public CompleteKey put(Key key, Map values) { - // TODO Auto-generated method stub - return null; - } - - public Map get(CompleteKey key) { - // TODO Auto-generated method stub - return null; - } - - public void delete(CompleteKey... key) { - // TODO Auto-generated method stub - - } - - public void allocateIds(Key... key) { - // TODO Auto-generated method stub - + return options; } } diff --git a/src/main/java/com/google/gcloud/datastore/IncompleteKey.java b/src/main/java/com/google/gcloud/datastore/IncompleteKey.java new file mode 100644 index 000000000000..36855b2bd9ba --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/IncompleteKey.java @@ -0,0 +1,313 @@ +package com.google.gcloud.datastore; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.gcloud.datastore.DatastoreServiceOptions.validateDataset; + +import com.google.api.services.datastore.DatastoreV1; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +public class IncompleteKey implements Serializable { + + private static final long serialVersionUID = -75301206578793347L; + + private final transient String dataset; + private final transient String namespace; + private final transient ImmutableList path; + private transient DatastoreV1.Key temp; // only for deserialization + + public static final class PathEntry implements Serializable { + + private static final long serialVersionUID = -7968078857690784595L; + + private final transient String kind; + private final transient Long id; + private final transient String name; + private transient DatastoreV1.Key.PathElement tempPathElementPb; // only for deserialization + + private PathEntry(String kind) { + this(kind, null); + } + + public PathEntry(String kind, long id) { + this.kind = kind; + this.id = id; + this.name = null; + } + + public PathEntry(String kind, String name) { + this.kind = kind; + this.name = name; + this.id = null; + } + + public String getKind() { + return kind; + } + + public boolean hasId() { + return id != null; + } + + public long getId() { + return id == null ? 0 : id; + } + + public boolean hasName() { + return name != null; + } + + public String getName() { + return name == null ? "" : name; + } + + public Object getNameOrId() { + return id == null ? name : id; + } + + @Override + public String toString() { + return toPb().toString(); + } + + @Override + public int hashCode() { + return Objects.hash(kind, id, name); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PathEntry)) { + return false; + } + PathEntry other = (PathEntry) obj; + return Objects.equals(kind, other.kind) + && Objects.equals(id, other.id) + && Objects.equals(name, other.name); + } + + static PathEntry fromPb(DatastoreV1.Key.PathElement pathElementPb) { + String kind = pathElementPb.getKind(); + if (pathElementPb.hasId()) { + return new PathEntry(kind, pathElementPb.getId()); + } else if (pathElementPb.hasName()) { + return new PathEntry(kind, pathElementPb.getName()); + } + return new PathEntry(kind); + } + + DatastoreV1.Key.PathElement toPb() { + DatastoreV1.Key.PathElement.Builder pathElementPb = DatastoreV1.Key.PathElement.newBuilder(); + pathElementPb.setKind(kind); + if (id != null) { + pathElementPb.setId(id); + } else if (name != null) { + pathElementPb.setName(name); + } + return pathElementPb.build(); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(toPb().toByteArray()); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + byte[] bytes = (byte[]) in.readObject(); + tempPathElementPb = DatastoreV1.Key.PathElement.parseFrom(bytes); + } + + @SuppressWarnings("unused") + private Object readResolve() throws ObjectStreamException { + return fromPb(tempPathElementPb); + } + } + + public static class Builder { + + private String dataset; + private String namespace = DEFAULT_NAMESPACE; + private String kind; + private ImmutableList.Builder path = ImmutableList.builder(); + + private static final String DEFAULT_NAMESPACE = ""; + + public Builder(String dataset, String kind) { + this.dataset = validateDataset(dataset); + this.kind = validateKind(kind); + } + + public Builder(IncompleteKey key) { + dataset = key.dataset; + namespace = key.namespace; + path = ImmutableList.builder().addAll(key.getAncestorPath()); + kind = key.getKind(); + } + + public Builder addToPath(String kind, long id) { + checkArgument(id != 0, "id must not be equal to zero"); + path.add(new PathEntry(kind, id)); + return this; + } + + public Builder addToPath(String kind, String name) { + checkArgument(Strings.isNullOrEmpty(name) , "name must not be empty or null"); + checkArgument(name.length() <= 500, "name must not exceed 500 characters"); + path.add(new PathEntry(kind, name)); + return this; + } + + public void addToPath(PathEntry pathEntry) { + path.add(pathEntry); + } + + public Builder setKind(String kind) { + this.kind = validateKind(kind); + return this; + } + + private String validateKind(String kind) { + checkArgument(Strings.isNullOrEmpty(kind), "kind must not be empty or null"); + checkArgument(kind.length() <= 500, "kind must not contain more than 500 characters"); + return kind; + } + + public Builder clearPath() { + path = ImmutableList.builder(); + return this; + } + + public Builder setDataset(String dataset) { + this.dataset = validateDataset(dataset); + return this; + } + + public Builder setNamespace(String namespace) { + this.namespace = checkNotNull(namespace); + return this; + } + + public IncompleteKey build() { + PathEntry leaf = new PathEntry(kind); + ImmutableList pathList = + ImmutableList.builder().addAll(path.build()).add(leaf).build(); + return new IncompleteKey(dataset, namespace, pathList); + } + } + + IncompleteKey(String dataset, String namespace, ImmutableList path) { + checkState(!path.isEmpty(), "path must not be empty"); + this.dataset = dataset; + this.namespace = namespace; + this.path = path; + } + + public String getDataset() { + return dataset; + } + + public String getNamespace() { + return namespace; + } + + protected List getFullPath() { + return path; + } + + public List getAncestorPath() { + return path.subList(0, path.size() - 1); + } + + public String getKind() { + return getLeaf().getKind(); + } + + @Override + public String toString() { + return toPb().toString(); + } + + @Override + public int hashCode() { + return Objects.hash(dataset, namespace, path); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof IncompleteKey)) { + return false; + } + IncompleteKey otherKey = (IncompleteKey) other; + return Objects.equals(dataset, otherKey.dataset) + && Objects.equals(namespace, otherKey.namespace) + && Objects.equals(path, otherKey.path); + } + + PathEntry getLeaf() { + return path.get(path.size() - 1); + } + + static IncompleteKey fromPb(DatastoreV1.Key keyPb) { + String dataset = null; + String namespace = null; + if (keyPb.hasPartitionId()) { + DatastoreV1.PartitionId partitionIdPb = keyPb.getPartitionId(); + if (partitionIdPb.hasDatasetId()) { + dataset = partitionIdPb.getDatasetId(); + } + if (partitionIdPb.hasNamespace()) { + namespace = partitionIdPb.getNamespace(); + } + } + ImmutableList.Builder pathBuilder = ImmutableList.builder(); + for (DatastoreV1.Key.PathElement pathElementPb : keyPb.getPathElementList()) { + pathBuilder.add(PathEntry.fromPb(pathElementPb)); + } + return new IncompleteKey(dataset, namespace, pathBuilder.build()); + } + + DatastoreV1.Key toPb() { + DatastoreV1.Key.Builder keyPb = DatastoreV1.Key.newBuilder(); + DatastoreV1.PartitionId.Builder partitionIdPb = DatastoreV1.PartitionId.newBuilder(); + if (dataset != null) { + partitionIdPb.setDatasetId(dataset); + } + if (namespace != null) { + partitionIdPb.setNamespace(namespace); + } + if (partitionIdPb.hasDatasetId() || partitionIdPb.hasNamespace()) { + keyPb.setPartitionId(partitionIdPb.build()); + } + for (PathEntry pathEntry : path) { + keyPb.addPathElement(pathEntry.toPb()); + } + return keyPb.build(); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(toPb().toByteArray()); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + byte[] bytes = (byte[]) in.readObject(); + temp = DatastoreV1.Key.parseFrom(bytes); + } + + @SuppressWarnings("unused") + private Object readResolve() throws ObjectStreamException { + return fromPb(temp); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index 0ad1f670c151..6e886da4283b 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -1,202 +1,81 @@ package com.google.gcloud.datastore; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.gcloud.datastore.DatastoreServiceOptions.validateDataset; +import static com.google.common.base.Preconditions.checkNotNull; import com.google.api.services.datastore.DatastoreV1; -import com.google.api.services.datastore.DatastoreV1.Key.PathElement; -import com.google.api.services.datastore.DatastoreV1.PartitionId; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; -import java.util.List; -import java.util.Objects; +/** + * A key that is guaranteed to be complete. + */ +public final class Key extends IncompleteKey { -public final class Key { + private static final long serialVersionUID = 3160994559785491356L; - private final String dataset; - private final String namespace; - private final ImmutableList path; - - public static final class PathEntry { - - private final String kind; - private final Long id; - private final String name; - - PathEntry(String kind) { - this.kind = kind; - this.id = null; - this.name = null; - } - - PathEntry(String kind, long id) { - checkArgument(id != 0, "id must not be equal to zero"); - this.kind = kind; - this.id = id; - this.name = null; - } + public static class Builder { - PathEntry(String kind, String name) { - checkArgument(Strings.isNullOrEmpty(name) , "name must not be empty or null"); - checkArgument(name.length() <= 500, "name must not exceed 500 characters"); - this.kind = kind; - this.name = name; - this.id = null; - } + private final IncompleteKey.Builder keyBuilder; + private String name; - public String getKind() { - return kind; + public Builder(String dataset, String kind) { + keyBuilder = new IncompleteKey.Builder(dataset, kind); } - public boolean hasId() { - return id != null; - } - - public long getId() { - return id == null ? 0 : id; - } + public Builder(Key key) { + keyBuilder = new IncompleteKey.Builder(key); - public boolean hasName() { - return name != null; + // set name or id } - public String getName() { - return name == null ? "" : name; - } - } - - public static class Builder { - - private String dataset; - private String namespace; - private ImmutableList.Builder path = ImmutableList.builder(); - - public Builder addChild(String kind, long id) { - path.add(new PathEntry(kind, id)); + public Builder setDataset(String dataset) { + keyBuilder.setDataset(checkNotNull(dataset)); return this; } - public Builder addChild(String kind, String name) { - path.add(new PathEntry(kind, name)); + public Builder setNamespace(String namespace) { + keyBuilder.setNamespace(checkNotNull(namespace)); return this; } - public Builder addChild(String kind) { - path.add(new PathEntry(kind)); + public Builder addToPath(String kind, long id) { + keyBuilder.addChild(kind, id); return this; } - public Builder setDataset(String dataset) { - this.dataset = dataset == null ? null : validateDataset(dataset); + public Builder addToPath(String kind, String name) { + keyBuilder.addChild(kind, name); return this; } - public Builder setNamespace(String namespace) { - this.namespace = namespace; + public Builder clearPath() { + keyBuilder.clearPath(); return this; } - public Key build() { - return new Key(dataset, namespace, path.build()); - } - } - - private Key(String dataset, String namespace, ImmutableList path) { - checkState(!path.isEmpty(), "path must not be empty"); - this.dataset = dataset; - this.namespace = namespace; - this.path = path; - } - - public String getDataset() { - return dataset; - } - - public String getNamespace() { - return namespace; - } - - public List getPath() { - return path; - } + public Key build(String name) { - public String getKind() { - return getLeaf().getKind(); + return new Key(keyBuilder.build()); + } } - @Override - public String toString() { - return toPb().toString(); + private Key(IncompleteKey key) { + super(key.getDataset(), key.getNamespace(), ImmutableList.copyOf(key.getFullPath())); } - @Override - public int hashCode() { - return Objects.hash(dataset, namespace, path); + public Long getId() { + return getLeaf().getId(); } - @Override - public boolean equals(Object other) { - if (!(other instanceof Key)) { - return false; - } - Key otherKey = (Key) other; - return Objects.equals(dataset, otherKey.dataset) - && Objects.equals(namespace, otherKey.namespace) - && Objects.equals(path, otherKey.path); + public String getName() { + return getLeaf().getName(); } - PathEntry getLeaf() { - return path.get(path.size() - 1); + public Object getNameOrId() { + PathEntry leaf = getLeaf(); + return leaf.hasId() ? leaf.getId() : leaf.getName(); } static Key fromPb(DatastoreV1.Key keyPb) { - Builder builder = new Builder(); - if (keyPb.hasPartitionId()) { - PartitionId partitionIdPb = keyPb.getPartitionId(); - if (partitionIdPb.hasDatasetId()) { - builder.setDataset(partitionIdPb.getDatasetId()); - } - if (partitionIdPb.hasNamespace()) { - builder.setNamespace(partitionIdPb.getNamespace()); - } - } - for (PathElement pathElementPb : keyPb.getPathElementList()) { - String kind = pathElementPb.getKind(); - if (pathElementPb.hasId()) { - builder.addChild(kind, pathElementPb.getId()); - } else if (pathElementPb.hasName()) { - builder.addChild(kind, pathElementPb.getName()); - } else { - builder.addChild(kind); - } - } - return builder.build(); - } - - DatastoreV1.Key toPb() { - DatastoreV1.Key.Builder keyPb = DatastoreV1.Key.newBuilder(); - PartitionId.Builder partitionIdPb = PartitionId.newBuilder(); - if (dataset != null) { - partitionIdPb.setDatasetId(dataset); - } - if (namespace != null) { - partitionIdPb.setNamespace(namespace); - } - if (partitionIdPb.hasDatasetId() || partitionIdPb.hasNamespace()) { - keyPb.setPartitionId(partitionIdPb.build()); - } - for (PathEntry pathEntry : path) { - PathElement.Builder pathElementPb = PathElement.newBuilder(); - pathElementPb.setKind(pathEntry.kind); - if (pathEntry.id != null) { - pathElementPb.setId(pathEntry.id); - } else if (pathEntry.name != null) { - pathElementPb.setName(pathEntry.name); - } - keyPb.addPathElement(pathElementPb.build()); - } - return keyPb.build(); + return new Key(IncompleteKey.fromPb(keyPb)); } } diff --git a/src/main/java/com/google/gcloud/datastore/KeyProperty.java b/src/main/java/com/google/gcloud/datastore/KeyProperty.java new file mode 100644 index 000000000000..e1a21df1c467 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/KeyProperty.java @@ -0,0 +1,55 @@ +package com.google.gcloud.datastore; + +import static com.google.api.services.datastore.DatastoreV1.Value.KEY_VALUE_FIELD_NUMBER; + +import com.google.api.services.datastore.DatastoreV1.Value; + +public final class KeyProperty extends Property { + + private static final long serialVersionUID = -1318353707326704821L; + + static final Marshaller MARSHALLER = + new BaseMarshaller() { + + @Override + public int getProtoFieldId() { + return KEY_VALUE_FIELD_NUMBER; + } + + @Override + protected Builder newBuilder(Value from) { + return new Builder(Key.fromPb(from.getKeyValue())); + } + + @Override + protected void setValueField(KeyProperty from, Value.Builder to) { + to.setKeyValue(from.getValue().toPb()); + } + }; + + public static final class Builder extends Property.BaseBuilder { + + public Builder(Key value) { + super(Type.KEY); + setValue(value); + } + + @Override + public KeyProperty build() { + return new KeyProperty(this); + } + + @Override + protected Builder self() { + return this; + } + } + + public KeyProperty(Key key) { + this(new Builder(key)); + } + + KeyProperty(Builder builder) { + super(builder); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/NullProperty.java b/src/main/java/com/google/gcloud/datastore/NullProperty.java index 0a9073995c6a..0ea395c391d7 100644 --- a/src/main/java/com/google/gcloud/datastore/NullProperty.java +++ b/src/main/java/com/google/gcloud/datastore/NullProperty.java @@ -6,6 +6,8 @@ public final class NullProperty extends Property { + private static final long serialVersionUID = 8497300779013002270L; + static final Marshaller MARSHALLER = new BaseMarshaller() { diff --git a/src/main/java/com/google/gcloud/datastore/Property.java b/src/main/java/com/google/gcloud/datastore/Property.java index c4a884ad9c39..11d8f182b6ec 100644 --- a/src/main/java/com/google/gcloud/datastore/Property.java +++ b/src/main/java/com/google/gcloud/datastore/Property.java @@ -6,26 +6,33 @@ import com.google.api.services.datastore.DatastoreV1.Value; import com.google.protobuf.Descriptors.FieldDescriptor; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.Serializable; import java.util.Objects; +// TODO: add javadoc, and mention that null should only be represented by NullValue. public abstract class - Property, B extends Property.Builder> { + Property, B extends Property.Builder> + implements Serializable { - private final Type type; - private final boolean indexed; - private final Integer meaning; - private final V value; + private static final long serialVersionUID = -1899638277588872742L; + + private transient final Type type; + private transient final boolean indexed; + private transient final Integer meaning; + private transient final V value; + private transient Value tempValuePb; // only for deserialization public enum Type { NULL(NullProperty.MARSHALLER), STRING(StringProperty.MARSHALLER), - PROPERTY_MAP(PropertyMapProperty.MARSHALLER); - // ListValue -> ListValueProperty - // CompleteKey -> CompleteKeyProperty - // COMPLETE_KEY_VALUE(CompleteKeyValue.PROVIDER, KEY_VALUE_FIELD_NUMBER), - //KEY_MAP_VALUE(KeyMapValue.MARSHALLER); - // List_VALUE(ListValue.class, LIST_VALUE_FIELD_NUMBER); + PROPERTY_MAP(PropertyMapProperty.MARSHALLER), + PROPERTY_LIST(PropertyListProperty.MARSHALLER), + KEY(KeyProperty.MARSHALLER); /* TODO: Also implement @@ -259,4 +266,21 @@ Value toPb() { // and the use of oneof which generates an enum of all possible values. return (Property) new NullProperty(); } + + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(toPb().toByteArray()); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + byte[] bytes = (byte[]) in.readObject(); + tempValuePb = Value.parseFrom(bytes); + } + + @SuppressWarnings("unused") + private Object readResolve() throws ObjectStreamException { + return fromPb(tempValuePb); + } } diff --git a/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java b/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java new file mode 100644 index 000000000000..72af5de1f7e8 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java @@ -0,0 +1,87 @@ +package com.google.gcloud.datastore; + +import static com.google.api.client.util.Preconditions.checkNotNull; +import static com.google.api.services.datastore.DatastoreV1.Value.LIST_VALUE_FIELD_NUMBER; + +import com.google.api.services.datastore.DatastoreV1.Value; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +public final class PropertyListProperty extends + Property>, PropertyListProperty, PropertyListProperty.Builder> { + + private static final long serialVersionUID = -5461475706792576395L; + + static final Marshaller>, PropertyListProperty, Builder> MARSHALLER = + new BaseMarshaller>, PropertyListProperty, Builder>() { + + @Override + public int getProtoFieldId() { + return LIST_VALUE_FIELD_NUMBER; + } + + @Override + protected Builder newBuilder(Value from) { + Builder builder = new Builder(); + for (Value valuePb : from.getListValueList()) { + builder.addProperty(Property.fromPb(valuePb)); + } + return builder; + } + + @Override + protected void setValueField(PropertyListProperty from, Value.Builder to) { + for (Property property : from.getValue()) { + to.addListValue(property.toPb()); + } + } + }; + + public static final class Builder extends + Property.BaseBuilder>, PropertyListProperty, Builder> { + + private ImmutableList.Builder> listBuilder = ImmutableList.builder(); + + public Builder() { + super(Type.PROPERTY_LIST); + setIndexed(false); + } + + public Builder addProperty(Property property) { + listBuilder.add(property); + return this; + } + + @Override + public Builder setValue(List> value) { + listBuilder = ImmutableList.>builder().addAll(checkNotNull(value)); + return this; + } + + @Override + public List> getValue() { + return listBuilder.build(); + } + + @Override + protected Builder self() { + return this; + } + + @Override + public PropertyListProperty build() { + Preconditions.checkState(!getValue().isEmpty(), "Property list could not be empty"); + return new PropertyListProperty(this); + } + } + + public PropertyListProperty(List> properties) { + this(new Builder().setValue(properties)); + } + + PropertyListProperty(Builder builder) { + super(builder); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/PropertyMapProperty.java b/src/main/java/com/google/gcloud/datastore/PropertyMapProperty.java index a66aa57910be..395deea72769 100644 --- a/src/main/java/com/google/gcloud/datastore/PropertyMapProperty.java +++ b/src/main/java/com/google/gcloud/datastore/PropertyMapProperty.java @@ -13,7 +13,9 @@ public final class PropertyMapProperty extends Property>, PropertyMapProperty, PropertyMapProperty.Builder> { - private final Key key; + private static final long serialVersionUID = -5461475706792576395L; + + private final IncompleteKey key; static final Marshaller>, PropertyMapProperty, Builder> MARSHALLER = new BaseMarshaller>, PropertyMapProperty, Builder>() { @@ -28,7 +30,7 @@ protected Builder newBuilder(Value from) { Builder builder = new Builder(); Entity entityPb = from.getEntityValue(); if (entityPb.hasKey()) { - builder.setKey(Key.fromPb(entityPb.getKey())); + builder.setKey(IncompleteKey.fromPb(entityPb.getKey())); } for (DatastoreV1.Property propertyPb : entityPb.getPropertyList()) { builder.addProperty(propertyPb.getName(), Property.fromPb(propertyPb.getValue())); @@ -52,7 +54,7 @@ protected void setValueField(PropertyMapProperty from, DatastoreV1.Value.Builder public static final class Builder extends Property.BaseBuilder>, PropertyMapProperty, Builder> { - private Key key; + private IncompleteKey key; private ImmutableMap.Builder> mapBuilder = ImmutableMap.builder(); public Builder() { @@ -60,11 +62,11 @@ public Builder() { setIndexed(false); } - public Key getKey() { + public IncompleteKey getKey() { return key; } - public Builder setKey(Key key) { + public Builder setKey(IncompleteKey key) { this.key = key; return this; } @@ -96,7 +98,7 @@ public PropertyMapProperty build() { } } - public PropertyMapProperty(Key key, Map> properties) { + public PropertyMapProperty(IncompleteKey key, Map> properties) { this(new Builder().setValue(properties).setKey(key)); } @@ -105,7 +107,7 @@ public PropertyMapProperty(Key key, Map> properties) { key = builder.key; } - public Key getKey() { + public IncompleteKey getKey() { return key; } } diff --git a/src/main/java/com/google/gcloud/datastore/StringProperty.java b/src/main/java/com/google/gcloud/datastore/StringProperty.java index df83288969d1..dd805cdb5297 100644 --- a/src/main/java/com/google/gcloud/datastore/StringProperty.java +++ b/src/main/java/com/google/gcloud/datastore/StringProperty.java @@ -5,10 +5,10 @@ import com.google.api.services.datastore.DatastoreV1.Value; -// TODO: add javadoc, find the right place to describe that null should only -// be represented by NullValue (so nulls are not allowed here). public final class StringProperty extends Property { + private static final long serialVersionUID = -3105699707394545523L; + static final Marshaller MARSHALLER = new BaseMarshaller() { From c5b5b75533e24c298692193e8f4a98d8102bdc86 Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 25 Nov 2014 17:36:17 -0800 Subject: [PATCH 018/771] adding entity --- .../gcloud/datastore/DatastoreService.java | 3 +- .../gcloud/datastore/EmbeddedEntity.java | 151 ++++++++++++++++++ .../datastore/EmbeddedEntityProperty.java | 63 ++++++++ .../com/google/gcloud/datastore/Entity.java | 143 +++++++++++++++++ .../gcloud/datastore/IncompleteKey.java | 71 ++++---- .../java/com/google/gcloud/datastore/Key.java | 111 +++++++++++-- .../google/gcloud/datastore/KeyProperty.java | 11 +- .../google/gcloud/datastore/NullProperty.java | 9 +- .../com/google/gcloud/datastore/Property.java | 13 +- .../datastore/PropertyListProperty.java | 26 ++- .../gcloud/datastore/PropertyMapProperty.java | 113 ------------- .../gcloud/datastore/StringProperty.java | 11 +- 12 files changed, 541 insertions(+), 184 deletions(-) create mode 100644 src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java create mode 100644 src/main/java/com/google/gcloud/datastore/EmbeddedEntityProperty.java create mode 100644 src/main/java/com/google/gcloud/datastore/Entity.java delete mode 100644 src/main/java/com/google/gcloud/datastore/PropertyMapProperty.java diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index b876b721df6b..6e09050be0f0 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -16,6 +16,7 @@ interface DatastoreReader { //QueryResult runQuery(Query query); } + // TODO: remove all refrence of IncomplteKey (except allocate and use Entity instead of Map) interface DatastoreWriter { @@ -69,8 +70,6 @@ public interface BatchOptions { Batch newBatch(); - P - Key allocateId(IncompleteKey key); // results are returned in request order diff --git a/src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java b/src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java new file mode 100644 index 000000000000..00d8c49c0973 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java @@ -0,0 +1,151 @@ +package com.google.gcloud.datastore; + +import com.google.api.services.datastore.DatastoreV1; +import com.google.common.collect.ImmutableMap; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public final class EmbeddedEntity implements Serializable { + + private static final long serialVersionUID = 6492561268709192891L; + + private final transient IncompleteKey key; + private final transient ImmutableMap> properties; + private transient DatastoreV1.Entity tempEntityPb; // only for deserialization + + public static final class Builder { + + private IncompleteKey key; + private Map> properties = new HashMap<>(); + + public Builder() { + } + + public Builder(EmbeddedEntity entity) { + this.key = entity.key; + this.properties = new HashMap<>(entity.properties); + } + + public Builder(Entity entity) { + this.key = entity.getKey(); + this.properties = new HashMap<>(entity.getProperties()); + } + + public Builder setKey(IncompleteKey key) { + this.key = key; + return this; + } + + public Builder clearProperties() { + properties.clear(); + return this; + } + + public Builder removeProperty(String name) { + properties.remove(name); + return this; + } + + public Builder setProperty(String name, Property property) { + properties.put(name, property); + return this; + } + + public EmbeddedEntity build() { + return new EmbeddedEntity(this); + } + } + + private EmbeddedEntity(Builder builder) { + key = builder.key; + properties = ImmutableMap.copyOf(builder.properties); + } + + /** + * Returns the key or null if not provided. + */ + public IncompleteKey getKey() { + return key; + } + + public boolean hasProperty(String name) { + return properties.containsKey(name); + } + + public Property getProperty(String name) { + return properties.get(name); + } + + public Set getPropertyNames() { + return properties.keySet(); + } + + @Override + public String toString() { + return toPb().toString(); + } + + @Override + public int hashCode() { + return Objects.hash(key, properties); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof EmbeddedEntity)) { + return false; + } + EmbeddedEntity other = (EmbeddedEntity) obj; + return Objects.equals(key, other.key) + && Objects.equals(properties, other.properties); + } + + static EmbeddedEntity fromPb(DatastoreV1.Entity entityPb) { + Builder builder = new Builder(); + if (entityPb.hasKey()) { + builder.setKey(IncompleteKey.fromPb(entityPb.getKey())); + } + for (DatastoreV1.Property property : entityPb.getPropertyList()) { + builder.setProperty(property.getName(), Property.fromPb(property.getValue())); + } + return builder.build(); + } + + DatastoreV1.Entity toPb() { + DatastoreV1.Entity.Builder entityPb = DatastoreV1.Entity.newBuilder(); + if (key != null) { + entityPb.setKey(key.toPb()); + } + for (Map.Entry> entry : properties.entrySet()) { + DatastoreV1.Property.Builder propertyPb = DatastoreV1.Property.newBuilder(); + propertyPb.setName(entry.getKey()); + propertyPb.setValue(entry.getValue().toPb()); + entityPb.addProperty(propertyPb.build()); + } + return entityPb.build(); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(toPb().toByteArray()); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + byte[] bytes = (byte[]) in.readObject(); + tempEntityPb = DatastoreV1.Entity.parseFrom(bytes); + } + + @SuppressWarnings("unused") + private Object readResolve() throws ObjectStreamException { + return fromPb(tempEntityPb); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/EmbeddedEntityProperty.java b/src/main/java/com/google/gcloud/datastore/EmbeddedEntityProperty.java new file mode 100644 index 000000000000..2f03ffbe2e78 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/EmbeddedEntityProperty.java @@ -0,0 +1,63 @@ +package com.google.gcloud.datastore; + +import static com.google.api.services.datastore.DatastoreV1.Value.ENTITY_VALUE_FIELD_NUMBER; + +import com.google.api.services.datastore.DatastoreV1; + +public final class EmbeddedEntityProperty extends + Property { + + private static final long serialVersionUID = -5461475706792576395L; + + static final Marshaller MARSHALLER = + new BaseMarshaller() { + + @Override + public int getProtoFieldId() { + return ENTITY_VALUE_FIELD_NUMBER; + } + + @Override + protected Builder newBuilder(EmbeddedEntity value) { + return new Builder(value); + } + + @Override + protected EmbeddedEntity getValue(DatastoreV1.Value from) { + return EmbeddedEntity.fromPb(from.getEntityValue()); + } + + @Override + protected void setValue(EmbeddedEntityProperty from, DatastoreV1.Value.Builder to) { + to.setEntityValue(from.getValue().toPb()); + } + }; + + public static final class Builder extends + Property.BaseBuilder { + + public Builder(EmbeddedEntity entity) { + super(Type.EMBEDDED_ENTITY); + setIndexed(false); + setValue(entity); + } + + @Override + protected Builder self() { + return this; + } + + @Override + public EmbeddedEntityProperty build() { + return new EmbeddedEntityProperty(this); + } + } + + public EmbeddedEntityProperty(EmbeddedEntity entity) { + this(new Builder(entity)); + } + + EmbeddedEntityProperty(Builder builder) { + super(builder); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/Entity.java b/src/main/java/com/google/gcloud/datastore/Entity.java new file mode 100644 index 000000000000..6051b4a3a32e --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/Entity.java @@ -0,0 +1,143 @@ +package com.google.gcloud.datastore; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.api.services.datastore.DatastoreV1; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public final class Entity implements Serializable { + + private static final long serialVersionUID = 432961565733066915L; + + private final transient Key key; + private final transient ImmutableMap> properties; + private transient DatastoreV1.Entity tempEntityPb; // only for deserialization + + public static final class Builder { + + private Key key; + private Map> properties; + + public Builder(Key key) { + this.key = checkNotNull(key); + this.properties = new HashMap<>(); + } + + public Builder(Entity entity) { + this.key = entity.key; + this.properties = new HashMap<>(entity.properties); + } + + public Builder clearProperties() { + properties.clear(); + return this; + } + + public Builder removeProperty(String name) { + properties.remove(name); + return this; + } + + public Builder setProperty(String name, Property property) { + properties.put(name, property); + return this; + } + + public Entity build() { + return new Entity(this); + } + } + + private Entity(Builder builder) { + key = builder.key; + properties = ImmutableMap.copyOf(builder.properties); + } + + public Key getKey() { + return key; + } + + public boolean hasProperty(String name) { + return properties.containsKey(name); + } + + public Property getProperty(String name) { + return properties.get(name); + } + + public Set getPropertyNames() { + return properties.keySet(); + } + + Map> getProperties() { + return properties; + } + + @Override + public String toString() { + return toPb().toString(); + } + + @Override + public int hashCode() { + return Objects.hash(key, properties); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Entity)) { + return false; + } + Entity other = (Entity) obj; + return Objects.equals(key, other.key) + && Objects.equals(properties, other.properties); + } + + static Entity fromPb(DatastoreV1.Entity entityPb) { + Preconditions.checkArgument(entityPb.hasKey()); + Builder builder = new Builder(Key.fromPb(entityPb.getKey())); + for (DatastoreV1.Property property : entityPb.getPropertyList()) { + builder.setProperty(property.getName(), Property.fromPb(property.getValue())); + } + return builder.build(); + } + + DatastoreV1.Entity toPb() { + DatastoreV1.Entity.Builder entityPb = DatastoreV1.Entity.newBuilder(); + entityPb.setKey(key.toPb()); + for (Map.Entry> entry : properties.entrySet()) { + DatastoreV1.Property.Builder propertyPb = DatastoreV1.Property.newBuilder(); + propertyPb.setName(entry.getKey()); + propertyPb.setValue(entry.getValue().toPb()); + entityPb.addProperty(propertyPb.build()); + } + return entityPb.build(); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(toPb().toByteArray()); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + byte[] bytes = (byte[]) in.readObject(); + tempEntityPb = DatastoreV1.Entity.parseFrom(bytes); + } + + @SuppressWarnings("unused") + private Object readResolve() throws ObjectStreamException { + return fromPb(tempEntityPb); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/IncompleteKey.java b/src/main/java/com/google/gcloud/datastore/IncompleteKey.java index 36855b2bd9ba..859694d5b9cf 100644 --- a/src/main/java/com/google/gcloud/datastore/IncompleteKey.java +++ b/src/main/java/com/google/gcloud/datastore/IncompleteKey.java @@ -6,6 +6,7 @@ import static com.google.gcloud.datastore.DatastoreServiceOptions.validateDataset; import com.google.api.services.datastore.DatastoreV1; +import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; @@ -14,6 +15,7 @@ import java.io.ObjectOutputStream; import java.io.ObjectStreamException; import java.io.Serializable; +import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -23,10 +25,10 @@ public class IncompleteKey implements Serializable { private final transient String dataset; private final transient String namespace; - private final transient ImmutableList path; - private transient DatastoreV1.Key temp; // only for deserialization + private final transient ImmutableList path; + private transient DatastoreV1.Key tempKeyPb; // only for deserialization - public static final class PathEntry implements Serializable { + public static final class PathElement implements Serializable { private static final long serialVersionUID = -7968078857690784595L; @@ -35,17 +37,17 @@ public static final class PathEntry implements Serializable { private final transient String name; private transient DatastoreV1.Key.PathElement tempPathElementPb; // only for deserialization - private PathEntry(String kind) { + private PathElement(String kind) { this(kind, null); } - public PathEntry(String kind, long id) { + public PathElement(String kind, long id) { this.kind = kind; this.id = id; this.name = null; } - public PathEntry(String kind, String name) { + public PathElement(String kind, String name) { this.kind = kind; this.name = name; this.id = null; @@ -87,23 +89,23 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (!(obj instanceof PathEntry)) { + if (!(obj instanceof PathElement)) { return false; } - PathEntry other = (PathEntry) obj; + PathElement other = (PathElement) obj; return Objects.equals(kind, other.kind) && Objects.equals(id, other.id) && Objects.equals(name, other.name); } - static PathEntry fromPb(DatastoreV1.Key.PathElement pathElementPb) { + static PathElement fromPb(DatastoreV1.Key.PathElement pathElementPb) { String kind = pathElementPb.getKind(); if (pathElementPb.hasId()) { - return new PathEntry(kind, pathElementPb.getId()); + return new PathElement(kind, pathElementPb.getId()); } else if (pathElementPb.hasName()) { - return new PathEntry(kind, pathElementPb.getName()); + return new PathElement(kind, pathElementPb.getName()); } - return new PathEntry(kind); + return new PathElement(kind); } DatastoreV1.Key.PathElement toPb() { @@ -134,14 +136,15 @@ private Object readResolve() throws ObjectStreamException { } } - public static class Builder { + public static final class Builder { private String dataset; private String namespace = DEFAULT_NAMESPACE; private String kind; - private ImmutableList.Builder path = ImmutableList.builder(); + private List path = new LinkedList<>(); private static final String DEFAULT_NAMESPACE = ""; + private static final int MAX_PATH = 100; public Builder(String dataset, String kind) { this.dataset = validateDataset(dataset); @@ -151,25 +154,25 @@ public Builder(String dataset, String kind) { public Builder(IncompleteKey key) { dataset = key.dataset; namespace = key.namespace; - path = ImmutableList.builder().addAll(key.getAncestorPath()); kind = key.getKind(); + path.addAll(key.getAncestorPath()); } public Builder addToPath(String kind, long id) { checkArgument(id != 0, "id must not be equal to zero"); - path.add(new PathEntry(kind, id)); - return this; + return addToPath(new PathElement(kind, id)); } public Builder addToPath(String kind, String name) { checkArgument(Strings.isNullOrEmpty(name) , "name must not be empty or null"); checkArgument(name.length() <= 500, "name must not exceed 500 characters"); - path.add(new PathEntry(kind, name)); - return this; + return addToPath(new PathElement(kind, name)); } - public void addToPath(PathEntry pathEntry) { + public Builder addToPath(PathElement pathEntry) { + Preconditions.checkState(path.size() < MAX_PATH, "path can have at most 100 elements"); path.add(pathEntry); + return this; } public Builder setKind(String kind) { @@ -184,7 +187,7 @@ private String validateKind(String kind) { } public Builder clearPath() { - path = ImmutableList.builder(); + path.clear(); return this; } @@ -199,14 +202,14 @@ public Builder setNamespace(String namespace) { } public IncompleteKey build() { - PathEntry leaf = new PathEntry(kind); - ImmutableList pathList = - ImmutableList.builder().addAll(path.build()).add(leaf).build(); + PathElement leaf = new PathElement(kind); + ImmutableList pathList = + ImmutableList.builder().addAll(path).add(leaf).build(); return new IncompleteKey(dataset, namespace, pathList); } } - IncompleteKey(String dataset, String namespace, ImmutableList path) { + IncompleteKey(String dataset, String namespace, ImmutableList path) { checkState(!path.isEmpty(), "path must not be empty"); this.dataset = dataset; this.namespace = namespace; @@ -221,11 +224,7 @@ public String getNamespace() { return namespace; } - protected List getFullPath() { - return path; - } - - public List getAncestorPath() { + public List getAncestorPath() { return path.subList(0, path.size() - 1); } @@ -254,7 +253,7 @@ public boolean equals(Object other) { && Objects.equals(path, otherKey.path); } - PathEntry getLeaf() { + PathElement getLeaf() { return path.get(path.size() - 1); } @@ -270,9 +269,9 @@ static IncompleteKey fromPb(DatastoreV1.Key keyPb) { namespace = partitionIdPb.getNamespace(); } } - ImmutableList.Builder pathBuilder = ImmutableList.builder(); + ImmutableList.Builder pathBuilder = ImmutableList.builder(); for (DatastoreV1.Key.PathElement pathElementPb : keyPb.getPathElementList()) { - pathBuilder.add(PathEntry.fromPb(pathElementPb)); + pathBuilder.add(PathElement.fromPb(pathElementPb)); } return new IncompleteKey(dataset, namespace, pathBuilder.build()); } @@ -289,7 +288,7 @@ DatastoreV1.Key toPb() { if (partitionIdPb.hasDatasetId() || partitionIdPb.hasNamespace()) { keyPb.setPartitionId(partitionIdPb.build()); } - for (PathEntry pathEntry : path) { + for (PathElement pathEntry : path) { keyPb.addPathElement(pathEntry.toPb()); } return keyPb.build(); @@ -303,11 +302,11 @@ private void writeObject(ObjectOutputStream out) throws IOException { private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); byte[] bytes = (byte[]) in.readObject(); - temp = DatastoreV1.Key.parseFrom(bytes); + tempKeyPb = DatastoreV1.Key.parseFrom(bytes); } @SuppressWarnings("unused") private Object readResolve() throws ObjectStreamException { - return fromPb(temp); + return fromPb(tempKeyPb); } } diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index 6e886da4283b..8e315a23a86c 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -1,9 +1,16 @@ package com.google.gcloud.datastore; import static com.google.common.base.Preconditions.checkNotNull; +import static java.nio.charset.StandardCharsets.UTF_8; import com.google.api.services.datastore.DatastoreV1; import com.google.common.collect.ImmutableList; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; /** * A key that is guaranteed to be complete. @@ -16,15 +23,26 @@ public static class Builder { private final IncompleteKey.Builder keyBuilder; private String name; + private Long id; + + public Builder(String dataset, String kind, String name) { + keyBuilder = new IncompleteKey.Builder(dataset, kind); + this.name = name; + } - public Builder(String dataset, String kind) { + public Builder(String dataset, String kind, long id) { keyBuilder = new IncompleteKey.Builder(dataset, kind); + this.id = id; } - public Builder(Key key) { + public Builder(IncompleteKey key, String name) { keyBuilder = new IncompleteKey.Builder(key); + this.name = name; + } - // set name or id + public Builder(IncompleteKey key, long id) { + keyBuilder = new IncompleteKey.Builder(key); + this.id = id; } public Builder setDataset(String dataset) { @@ -37,13 +55,25 @@ public Builder setNamespace(String namespace) { return this; } + public Builder setName(String name) { + this.name = name; + this.id = null; + return this; + } + + public Builder setId(long id) { + this.id = id; + this.name = null; + return this; + } + public Builder addToPath(String kind, long id) { - keyBuilder.addChild(kind, id); + keyBuilder.addToPath(kind, id); return this; } public Builder addToPath(String kind, String name) { - keyBuilder.addChild(kind, name); + keyBuilder.addToPath(kind, name); return this; } @@ -52,14 +82,18 @@ public Builder clearPath() { return this; } - public Key build(String name) { - - return new Key(keyBuilder.build()); + public Key build() { + IncompleteKey key = keyBuilder.build(); + return id == null ? new Key(key, name) : new Key(key, id); } } - private Key(IncompleteKey key) { - super(key.getDataset(), key.getNamespace(), ImmutableList.copyOf(key.getFullPath())); + private Key(IncompleteKey key, String name) { + super(key.getDataset(), key.getNamespace(), newPath(key, name)); + } + + private Key(IncompleteKey key, long id) { + super(key.getDataset(), key.getNamespace(), newPath(key, id)); } public Long getId() { @@ -71,11 +105,64 @@ public String getName() { } public Object getNameOrId() { - PathEntry leaf = getLeaf(); + PathElement leaf = getLeaf(); return leaf.hasId() ? leaf.getId() : leaf.getName(); } + public String toUrlSafe() { + try { + return URLEncoder.encode(toString(), UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unxpeced encoding exception", e); + } + } + + public static Key fromUrlSafe(String urlSafe) { + try { + String utf8Str = URLDecoder.decode(urlSafe, UTF_8.name()); + DatastoreV1.Key keyPb = DatastoreV1.Key.parseFrom(ByteString.copyFromUtf8(utf8Str)); + return Key.fromPb(keyPb); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unxpeced decoding exception", e); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException("Could not parse key", e); + } + } + + /** + * Convert an {@code IncompleteKey} to a {@code Key} provided that the key has + * either name or id (complete). + + * @throws IllegalArgumentException if provided key is not complete. + */ + public static Key fromIncompleteKey(IncompleteKey key) { + if (key instanceof Key) { + return (Key) key; + } + PathElement leaf = key.getLeaf(); + if (leaf.hasId()) { + return new Key(key, leaf.getId()); + } else if (leaf.hasName()) { + return new Key(key, leaf.getName()); + } + throw new IllegalArgumentException("Key is missing name or id"); + } + static Key fromPb(DatastoreV1.Key keyPb) { - return new Key(IncompleteKey.fromPb(keyPb)); + return fromIncompleteKey(IncompleteKey.fromPb(keyPb)); + } + + private static ImmutableList newPath(IncompleteKey key, String name) { + ImmutableList.Builder path = ImmutableList.builder(); + path.addAll(key.getAncestorPath()); + path.add(new PathElement(key.getKind(), name)); + return path.build(); + } + + private static ImmutableList newPath(IncompleteKey key, long id) { + ImmutableList.Builder path = ImmutableList.builder(); + path.addAll(key.getAncestorPath()); + path.add(new PathElement(key.getKind(), id)); + return path.build(); } } diff --git a/src/main/java/com/google/gcloud/datastore/KeyProperty.java b/src/main/java/com/google/gcloud/datastore/KeyProperty.java index e1a21df1c467..95e5eb9eef58 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyProperty.java +++ b/src/main/java/com/google/gcloud/datastore/KeyProperty.java @@ -17,12 +17,17 @@ public int getProtoFieldId() { } @Override - protected Builder newBuilder(Value from) { - return new Builder(Key.fromPb(from.getKeyValue())); + protected Builder newBuilder(Key key) { + return new Builder(key); } @Override - protected void setValueField(KeyProperty from, Value.Builder to) { + protected Key getValue(Value from) { + return Key.fromPb(from.getKeyValue()); + } + + @Override + protected void setValue(KeyProperty from, Value.Builder to) { to.setKeyValue(from.getValue().toPb()); } }; diff --git a/src/main/java/com/google/gcloud/datastore/NullProperty.java b/src/main/java/com/google/gcloud/datastore/NullProperty.java index 0ea395c391d7..ba1861bc4b2e 100644 --- a/src/main/java/com/google/gcloud/datastore/NullProperty.java +++ b/src/main/java/com/google/gcloud/datastore/NullProperty.java @@ -12,7 +12,7 @@ public final class NullProperty extends Property() { @Override - public Builder newBuilder(Value from) { + public Builder newBuilder(Void value) { return new Builder(); } @@ -22,7 +22,12 @@ public int getProtoFieldId() { } @Override - protected void setValueField(NullProperty from, Value.Builder to) { + protected Void getValue(Value from) { + return null; + } + + @Override + protected void setValue(NullProperty from, Value.Builder to) { // nothing to set } }; diff --git a/src/main/java/com/google/gcloud/datastore/Property.java b/src/main/java/com/google/gcloud/datastore/Property.java index 11d8f182b6ec..6632b8bf4678 100644 --- a/src/main/java/com/google/gcloud/datastore/Property.java +++ b/src/main/java/com/google/gcloud/datastore/Property.java @@ -30,7 +30,7 @@ public enum Type { NULL(NullProperty.MARSHALLER), STRING(StringProperty.MARSHALLER), - PROPERTY_MAP(PropertyMapProperty.MARSHALLER), + EMBEDDED_ENTITY(EmbeddedEntityProperty.MARSHALLER), PROPERTY_LIST(PropertyListProperty.MARSHALLER), KEY(KeyProperty.MARSHALLER); @@ -101,7 +101,7 @@ abstract static class BaseMarshaller, B extends B @Override public final B fromProto(Value proto) { - B builder = newBuilder(proto); + B builder = newBuilder(getValue(proto)); builder.setIndexed(proto.getIndexed()); if (proto.hasMeaning()) { builder.setMeaning(proto.getMeaning()); @@ -116,13 +116,16 @@ public final Value toProto(P property) { if (property.getMeaning() != null) { builder.setMeaning(property.getMeaning()); } - setValueField(property, builder); + setValue(property, builder); return builder.build(); } - protected abstract B newBuilder(Value from); + // Move to a Builder Factory + protected abstract B newBuilder(V value); - protected abstract void setValueField(P from, Value.Builder to); + protected abstract V getValue(Value from); + + protected abstract void setValue(P from, Value.Builder to); } abstract static class BaseBuilder, B extends BaseBuilder> diff --git a/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java b/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java index 72af5de1f7e8..6bf9290e41e5 100644 --- a/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java +++ b/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java @@ -1,12 +1,12 @@ package com.google.gcloud.datastore; -import static com.google.api.client.util.Preconditions.checkNotNull; import static com.google.api.services.datastore.DatastoreV1.Value.LIST_VALUE_FIELD_NUMBER; import com.google.api.services.datastore.DatastoreV1.Value; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import java.util.ArrayList; import java.util.List; public final class PropertyListProperty extends @@ -23,16 +23,21 @@ public int getProtoFieldId() { } @Override - protected Builder newBuilder(Value from) { - Builder builder = new Builder(); + protected Builder newBuilder(List> properties) { + return new Builder().setValue(properties); + } + + @Override + protected List> getValue(Value from) { + List> properties = new ArrayList<>(from.getListValueCount()); for (Value valuePb : from.getListValueList()) { - builder.addProperty(Property.fromPb(valuePb)); + properties.add(Property.fromPb(valuePb)); } - return builder; + return properties; } @Override - protected void setValueField(PropertyListProperty from, Value.Builder to) { + protected void setValue(PropertyListProperty from, Value.Builder to) { for (Property property : from.getValue()) { to.addListValue(property.toPb()); } @@ -50,13 +55,18 @@ public Builder() { } public Builder addProperty(Property property) { + Preconditions.checkArgument( + property.getType() != Type.PROPERTY_LIST, "Cannot contain another list"); listBuilder.add(property); return this; } @Override - public Builder setValue(List> value) { - listBuilder = ImmutableList.>builder().addAll(checkNotNull(value)); + public Builder setValue(List> properties) { + listBuilder = ImmutableList.>builder(); + for (Property property : properties) { + addProperty(property); + } return this; } diff --git a/src/main/java/com/google/gcloud/datastore/PropertyMapProperty.java b/src/main/java/com/google/gcloud/datastore/PropertyMapProperty.java deleted file mode 100644 index 395deea72769..000000000000 --- a/src/main/java/com/google/gcloud/datastore/PropertyMapProperty.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.google.gcloud.datastore; - -import static com.google.api.client.util.Preconditions.checkNotNull; -import static com.google.api.services.datastore.DatastoreV1.Value.ENTITY_VALUE_FIELD_NUMBER; - -import com.google.api.services.datastore.DatastoreV1; -import com.google.api.services.datastore.DatastoreV1.Entity; -import com.google.api.services.datastore.DatastoreV1.Value; -import com.google.common.collect.ImmutableMap; - -import java.util.Map; - -public final class PropertyMapProperty extends - Property>, PropertyMapProperty, PropertyMapProperty.Builder> { - - private static final long serialVersionUID = -5461475706792576395L; - - private final IncompleteKey key; - - static final Marshaller>, PropertyMapProperty, Builder> MARSHALLER = - new BaseMarshaller>, PropertyMapProperty, Builder>() { - - @Override - public int getProtoFieldId() { - return ENTITY_VALUE_FIELD_NUMBER; - } - - @Override - protected Builder newBuilder(Value from) { - Builder builder = new Builder(); - Entity entityPb = from.getEntityValue(); - if (entityPb.hasKey()) { - builder.setKey(IncompleteKey.fromPb(entityPb.getKey())); - } - for (DatastoreV1.Property propertyPb : entityPb.getPropertyList()) { - builder.addProperty(propertyPb.getName(), Property.fromPb(propertyPb.getValue())); - } - return builder; - } - - @Override - protected void setValueField(PropertyMapProperty from, DatastoreV1.Value.Builder to) { - Entity.Builder entityPb = Entity.newBuilder(); - entityPb.setKey(from.getKey().toPb()); - for (Map.Entry> entry : from.getValue().entrySet()) { - Property property = entry.getValue(); - DatastoreV1.Property.Builder propertyPb = DatastoreV1.Property.newBuilder(); - propertyPb.setName(entry.getKey()); - propertyPb.setValue(property.toPb()); - } - } - }; - - public static final class Builder extends - Property.BaseBuilder>, PropertyMapProperty, Builder> { - - private IncompleteKey key; - private ImmutableMap.Builder> mapBuilder = ImmutableMap.builder(); - - public Builder() { - super(Type.PROPERTY_MAP); - setIndexed(false); - } - - public IncompleteKey getKey() { - return key; - } - - public Builder setKey(IncompleteKey key) { - this.key = key; - return this; - } - - public Builder addProperty(String name, Property property) { - mapBuilder.put(name, property); - return this; - } - - @Override - public Builder setValue(Map> value) { - mapBuilder = ImmutableMap.>builder().putAll(checkNotNull(value)); - return this; - } - - @Override - public Map> getValue() { - return mapBuilder.build(); - } - - @Override - protected Builder self() { - return this; - } - - @Override - public PropertyMapProperty build() { - return new PropertyMapProperty(this); - } - } - - public PropertyMapProperty(IncompleteKey key, Map> properties) { - this(new Builder().setValue(properties).setKey(key)); - } - - PropertyMapProperty(Builder builder) { - super(builder); - key = builder.key; - } - - public IncompleteKey getKey() { - return key; - } -} diff --git a/src/main/java/com/google/gcloud/datastore/StringProperty.java b/src/main/java/com/google/gcloud/datastore/StringProperty.java index dd805cdb5297..319d57144a50 100644 --- a/src/main/java/com/google/gcloud/datastore/StringProperty.java +++ b/src/main/java/com/google/gcloud/datastore/StringProperty.java @@ -18,12 +18,17 @@ public int getProtoFieldId() { } @Override - protected Builder newBuilder(Value from) { - return new Builder(from.getStringValue()); + protected Builder newBuilder(String value) { + return new Builder(value); } @Override - protected void setValueField(StringProperty from, Value.Builder to) { + protected String getValue(Value from) { + return from.getStringValue(); + } + + @Override + protected void setValue(StringProperty from, Value.Builder to) { to.setStringValue(from.getValue()); } }; From d0e243c2c9366a2927cb6938fa3109ddddf4cef0 Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 26 Nov 2014 17:47:26 -0800 Subject: [PATCH 019/771] work in progress --- .classpath | 1 + pom.xml | 76 ++++++----- .../com/google/gcloud/ServiceOptions.java | 6 +- .../gcloud/datastore/DatastoreReader.java | 14 +++ .../gcloud/datastore/DatastoreService.java | 48 ++----- .../datastore/DatastoreServiceImpl.java | 62 +++++++++ .../datastore/DatastoreServiceOptions.java | 16 ++- .../gcloud/datastore/DatastoreWriter.java | 12 ++ .../gcloud/datastore/EmbeddedEntity.java | 11 +- .../datastore/EmbeddedEntityProperty.java | 15 +-- .../com/google/gcloud/datastore/Entity.java | 8 +- .../gcloud/datastore/IncompleteKey.java | 4 +- .../java/com/google/gcloud/datastore/Key.java | 7 ++ .../google/gcloud/datastore/KeyProperty.java | 13 +- .../google/gcloud/datastore/NullProperty.java | 11 +- .../com/google/gcloud/datastore/Property.java | 119 ++++++++++-------- .../datastore/PropertyListProperty.java | 37 +++--- .../gcloud/datastore/StringProperty.java | 17 +-- .../gcloud/datastore/SerializationTest.java | 97 ++++++++++++++ 19 files changed, 394 insertions(+), 180 deletions(-) create mode 100644 src/main/java/com/google/gcloud/datastore/DatastoreReader.java create mode 100644 src/main/java/com/google/gcloud/datastore/DatastoreWriter.java create mode 100644 src/test/java/com/google/gcloud/datastore/SerializationTest.java diff --git a/.classpath b/.classpath index 70f23326e0d2..09fe957d54f4 100644 --- a/.classpath +++ b/.classpath @@ -23,6 +23,7 @@ + diff --git a/pom.xml b/pom.xml index 534629a90885..d82863493cd8 100644 --- a/pom.xml +++ b/pom.xml @@ -4,32 +4,52 @@ git-demo 0.0.1-SNAPSHOT - - com.google.http-client - google-http-client - 1.18.0-rc - - - com.google.oauth-client - google-oauth-client - 1.18.0-rc - - - com.google.guava - guava - RELEASE - - - com.google.apis - - google-api-services-datastore-protobuf - - v1beta2-rev1-2.1.0 - - - com.google.api-client - google-api-client-appengine - 1.18.0-rc - + + com.google.http-client + google-http-client + 1.18.0-rc + + + com.google.oauth-client + google-oauth-client + 1.18.0-rc + + + com.google.guava + guava + RELEASE + + + com.google.apis + + google-api-services-datastore-protobuf + + v1beta2-rev1-2.1.0 + + + com.google.api-client + google-api-client-appengine + 1.18.0-rc + + + junit + junit + test + RELEASE + - \ No newline at end of file + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + 1.7 + 1.7 + UTF-8 + + + + + diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java index 974cfcddae1f..137b9856bf0d 100644 --- a/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/src/main/java/com/google/gcloud/ServiceOptions.java @@ -79,17 +79,17 @@ protected Builder(ServiceOptions options) { protected abstract ServiceOptions build(); - public Builder setHost(String host) { + public Builder host(String host) { this.host = host; return this; } - public Builder setHttpTransport(HttpTransport httpTransport) { + public Builder httpTransport(HttpTransport httpTransport) { this.httpTransport = httpTransport; return this; } - public Builder setAuthConfig(AuthConfig authConfig) { + public Builder authConfig(AuthConfig authConfig) { this.authConfig = authConfig; return this; } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java new file mode 100644 index 000000000000..a1eb56aab46a --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java @@ -0,0 +1,14 @@ +package com.google.gcloud.datastore; + +import java.util.Iterator; + +public interface DatastoreReader { + + Entity get(Key key); + + // results are returned using request order + Iterator get(Key... key); + + // query result item is a tuple of (key, value...) where values may be empty + //QueryResult runQuery(Query query); +} \ No newline at end of file diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index 6e09050be0f0..ca4c55033322 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -1,32 +1,11 @@ package com.google.gcloud.datastore; import java.util.Iterator; -import java.util.Map; -public interface DatastoreService { +public interface DatastoreService extends DatastoreReader, DatastoreWriter { - interface DatastoreReader { + interface Query { - Map> get(Key key); - - // return the result in the given order - Iterator>> get(Iterator key); - - // query result item is a tuple of (key, value...) where values may be empty - //QueryResult runQuery(Query query); - } - - // TODO: remove all refrence of IncomplteKey (except allocate and use Entity instead of Map) - - interface DatastoreWriter { - - Key add(IncompleteKey key, Map> values); - - void update(Key key , Map> values); - - Key put(IncompleteKey key, Map> values); - - void delete(Key key); } @@ -43,35 +22,28 @@ enum IsolationLevel { SERIALIZABLE, SNAPSHOT; } - IsolationLevel getIsolationLevel(); - } - - public interface Batch extends DatastoreWriter { - @Override - void add(Key key, Map> values); + IsolationLevel getIsolationLevel(); - @Override - void update(Key key , Map> values); + boolean force(); + } - @Override - void put(Key key, Map> values); + public interface BatchWriter extends DatastoreWriter { void submit(); } public interface BatchOptions { - } DatastoreServiceOptions getOptions(); - Transaction newTransaction(TransactionOptions tsOptions); + Transaction newTransaction(TransactionOptions transactionOptions); - Batch newBatch(); + BatchWriter newBatchWriter(BatchOptions batchOptions); Key allocateId(IncompleteKey key); - // results are returned in request order - Iterator allocateIds(Iterator key); + // results are returned using request order + Iterator allocateIds(IncompleteKey... key); } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index f324081edaad..cc5ed9fa3434 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -1,5 +1,7 @@ package com.google.gcloud.datastore; +import java.util.Iterator; + final class DatastoreServiceImpl implements DatastoreService { @@ -13,4 +15,64 @@ final class DatastoreServiceImpl implements DatastoreService { public DatastoreServiceOptions getOptions() { return options; } + + @Override + public Transaction newTransaction(TransactionOptions transactionOptions) { + // TODO Auto-generated method stub + return null; + } + + @Override + public BatchWriter newBatchWriter(BatchOptions batchOptions) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Key allocateId(IncompleteKey key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Iterator allocateIds(IncompleteKey... key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Entity get(Key key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Iterator get(Key... key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void add(Entity... entity) { + // TODO Auto-generated method stub + + } + + @Override + public void update(Entity... entity) { + // TODO Auto-generated method stub + + } + + @Override + public Key put(Entity... entity) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void delete(Key... key) { + // TODO Auto-generated method stub + + } } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java index e9a3f9183c0e..bf2a58978fc1 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java @@ -17,23 +17,28 @@ public class DatastoreServiceOptions extends ServiceOptions { private static final Set SCOPES = ImmutableSet.of(DATASTORE_SCOPE, USERINFO_SCOPE); private static final Pattern PATTERN = Pattern.compile( "([a-z\\d\\-]{1,100}~)?([a-z\\d][a-z\\d\\-\\.]{0,99}\\:)?([a-z\\d][a-z\\d\\-]{0,99})"); + private final String dataset; + private final boolean force; DatastoreServiceOptions(Builder builder) { super(builder); dataset = firstNonNull(builder.dataset, getAppEngineAppId()); checkArgument(dataset != null, "missing dataset"); + force = builder.force; } public static class Builder extends ServiceOptions.Builder { private String dataset; + private boolean force = false; public Builder() {} public Builder(DatastoreServiceOptions options) { super(options); dataset = options.dataset; + force = options.force; } @Override @@ -41,10 +46,15 @@ public DatastoreServiceOptions build() { return new DatastoreServiceOptions(this); } - public Builder setDataset(String dataset) { + public Builder dataset(String dataset) { this.dataset = validateDataset(dataset); return this; } + + public Builder force(boolean force) { + this.force = force; + return this; + } } static String validateDataset(String dataset) { @@ -62,6 +72,10 @@ public String getDataset() { return dataset; } + public boolean getForce() { + return force; + } + @Override protected Set getScopes() { return SCOPES; diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java b/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java new file mode 100644 index 000000000000..005df9ff6800 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java @@ -0,0 +1,12 @@ +package com.google.gcloud.datastore; + +public interface DatastoreWriter { + + void add(Entity... entity); + + void update(Entity... entity); + + Key put(Entity... entity); + + void delete(Key... key); +} \ No newline at end of file diff --git a/src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java b/src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java index 00d8c49c0973..853d4edf5d55 100644 --- a/src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java +++ b/src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java @@ -1,7 +1,7 @@ package com.google.gcloud.datastore; import com.google.api.services.datastore.DatastoreV1; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSortedMap; import java.io.IOException; import java.io.ObjectInputStream; @@ -18,7 +18,7 @@ public final class EmbeddedEntity implements Serializable { private static final long serialVersionUID = 6492561268709192891L; private final transient IncompleteKey key; - private final transient ImmutableMap> properties; + private final transient ImmutableSortedMap> properties; private transient DatastoreV1.Entity tempEntityPb; // only for deserialization public static final class Builder { @@ -66,7 +66,12 @@ public EmbeddedEntity build() { private EmbeddedEntity(Builder builder) { key = builder.key; - properties = ImmutableMap.copyOf(builder.properties); + properties = ImmutableSortedMap.copyOf(builder.properties); + } + + public EmbeddedEntity(Entity entity) { + key = entity.getKey(); + properties = entity.getProperties(); } /** diff --git a/src/main/java/com/google/gcloud/datastore/EmbeddedEntityProperty.java b/src/main/java/com/google/gcloud/datastore/EmbeddedEntityProperty.java index 2f03ffbe2e78..d1d653ac9f07 100644 --- a/src/main/java/com/google/gcloud/datastore/EmbeddedEntityProperty.java +++ b/src/main/java/com/google/gcloud/datastore/EmbeddedEntityProperty.java @@ -9,7 +9,7 @@ public final class EmbeddedEntityProperty extends private static final long serialVersionUID = -5461475706792576395L; - static final Marshaller MARSHALLER = + static final BaseMarshaller MARSHALLER = new BaseMarshaller() { @Override @@ -18,7 +18,7 @@ public int getProtoFieldId() { } @Override - protected Builder newBuilder(EmbeddedEntity value) { + public Builder newBuilder(EmbeddedEntity value) { return new Builder(value); } @@ -29,7 +29,7 @@ protected EmbeddedEntity getValue(DatastoreV1.Value from) { @Override protected void setValue(EmbeddedEntityProperty from, DatastoreV1.Value.Builder to) { - to.setEntityValue(from.getValue().toPb()); + to.setEntityValue(from.get().toPb()); } }; @@ -38,13 +38,8 @@ public static final class Builder extends public Builder(EmbeddedEntity entity) { super(Type.EMBEDDED_ENTITY); - setIndexed(false); - setValue(entity); - } - - @Override - protected Builder self() { - return this; + indexed(false); + set(entity); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/Entity.java b/src/main/java/com/google/gcloud/datastore/Entity.java index 6051b4a3a32e..84f42b434ced 100644 --- a/src/main/java/com/google/gcloud/datastore/Entity.java +++ b/src/main/java/com/google/gcloud/datastore/Entity.java @@ -4,7 +4,7 @@ import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSortedMap; import java.io.IOException; import java.io.ObjectInputStream; @@ -21,7 +21,7 @@ public final class Entity implements Serializable { private static final long serialVersionUID = 432961565733066915L; private final transient Key key; - private final transient ImmutableMap> properties; + private final transient ImmutableSortedMap> properties; private transient DatastoreV1.Entity tempEntityPb; // only for deserialization public static final class Builder { @@ -61,7 +61,7 @@ public Entity build() { private Entity(Builder builder) { key = builder.key; - properties = ImmutableMap.copyOf(builder.properties); + properties = ImmutableSortedMap.copyOf(builder.properties); } public Key getKey() { @@ -80,7 +80,7 @@ public Set getPropertyNames() { return properties.keySet(); } - Map> getProperties() { + ImmutableSortedMap> getProperties() { return properties; } diff --git a/src/main/java/com/google/gcloud/datastore/IncompleteKey.java b/src/main/java/com/google/gcloud/datastore/IncompleteKey.java index 859694d5b9cf..8e67a39b5f0d 100644 --- a/src/main/java/com/google/gcloud/datastore/IncompleteKey.java +++ b/src/main/java/com/google/gcloud/datastore/IncompleteKey.java @@ -181,7 +181,7 @@ public Builder setKind(String kind) { } private String validateKind(String kind) { - checkArgument(Strings.isNullOrEmpty(kind), "kind must not be empty or null"); + checkArgument(!Strings.isNullOrEmpty(kind), "kind must not be empty or null"); checkArgument(kind.length() <= 500, "kind must not contain more than 500 characters"); return kind; } @@ -306,7 +306,7 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE } @SuppressWarnings("unused") - private Object readResolve() throws ObjectStreamException { + protected Object readResolve() throws ObjectStreamException { return fromPb(tempKeyPb); } } diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index 8e315a23a86c..adad267b27b2 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -8,6 +8,7 @@ import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; +import java.io.ObjectStreamException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; @@ -152,6 +153,12 @@ static Key fromPb(DatastoreV1.Key keyPb) { return fromIncompleteKey(IncompleteKey.fromPb(keyPb)); } + @Override + @SuppressWarnings("unused") + protected Object readResolve() throws ObjectStreamException { + return fromIncompleteKey((IncompleteKey) super.readResolve()); + } + private static ImmutableList newPath(IncompleteKey key, String name) { ImmutableList.Builder path = ImmutableList.builder(); path.addAll(key.getAncestorPath()); diff --git a/src/main/java/com/google/gcloud/datastore/KeyProperty.java b/src/main/java/com/google/gcloud/datastore/KeyProperty.java index 95e5eb9eef58..fda5daa11dcb 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyProperty.java +++ b/src/main/java/com/google/gcloud/datastore/KeyProperty.java @@ -8,7 +8,7 @@ public final class KeyProperty extends Property MARSHALLER = + static final BaseMarshaller MARSHALLER = new BaseMarshaller() { @Override @@ -17,7 +17,7 @@ public int getProtoFieldId() { } @Override - protected Builder newBuilder(Key key) { + public Builder newBuilder(Key key) { return new Builder(key); } @@ -28,7 +28,7 @@ protected Key getValue(Value from) { @Override protected void setValue(KeyProperty from, Value.Builder to) { - to.setKeyValue(from.getValue().toPb()); + to.setKeyValue(from.get().toPb()); } }; @@ -36,18 +36,13 @@ public static final class Builder extends Property.BaseBuilder MARSHALLER = + static final BaseMarshaller MARSHALLER = new BaseMarshaller() { @Override @@ -44,19 +44,14 @@ public NullProperty build() { } @Override - public Builder setValue(Void value) { + public Builder set(Void value) { checkArgument(value == null, "Only null values are allowed"); return this; } - - @Override - protected Builder self() { - return this; - } } public NullProperty() { - this(new Builder()); + this(new Builder().indexed(true)); } NullProperty(Builder builder) { diff --git a/src/main/java/com/google/gcloud/datastore/Property.java b/src/main/java/com/google/gcloud/datastore/Property.java index 6632b8bf4678..50ee7ca79834 100644 --- a/src/main/java/com/google/gcloud/datastore/Property.java +++ b/src/main/java/com/google/gcloud/datastore/Property.java @@ -13,26 +13,26 @@ import java.io.Serializable; import java.util.Objects; -// TODO: add javadoc, and mention that null should only be represented by NullValue. +// TODO: add javadoc, and mention that null should only be represented by NullValue. public abstract class Property, B extends Property.Builder> implements Serializable { private static final long serialVersionUID = -1899638277588872742L; - private transient final Type type; - private transient final boolean indexed; - private transient final Integer meaning; - private transient final V value; + private final transient Type type; + private final transient boolean indexed; + private final transient Integer meaning; + private final transient V value; private transient Value tempValuePb; // only for deserialization public enum Type { - NULL(NullProperty.MARSHALLER), - STRING(StringProperty.MARSHALLER), - EMBEDDED_ENTITY(EmbeddedEntityProperty.MARSHALLER), - PROPERTY_LIST(PropertyListProperty.MARSHALLER), - KEY(KeyProperty.MARSHALLER); + NULL(NullProperty.MARSHALLER, NullProperty.MARSHALLER), + STRING(StringProperty.MARSHALLER, StringProperty.MARSHALLER), + EMBEDDED_ENTITY(EmbeddedEntityProperty.MARSHALLER, EmbeddedEntityProperty.MARSHALLER), + PROPERTY_LIST(PropertyListProperty.MARSHALLER, PropertyListProperty.MARSHALLER), + KEY(KeyProperty.MARSHALLER, KeyProperty.MARSHALLER); /* TODO: Also implement @@ -46,13 +46,14 @@ public enum Type { // GEO_POINT(GeoPointValue.class, 8) // Does not seem to be public yet... */ - @SuppressWarnings("rawtypes") - private final Marshaller marshaller; + @SuppressWarnings("rawtypes") private final BuilderFactory builderFactory; + @SuppressWarnings("rawtypes") private final Marshaller marshaller; private FieldDescriptor field; , B extends Builder> - Type(Marshaller marshaller) { + Type(Marshaller marshaller, BuilderFactory builderFactory) { this.marshaller = marshaller; + this.builderFactory = builderFactory; field = Value.getDescriptor().findFieldByNumber(marshaller.getProtoFieldId()); } @@ -61,28 +62,38 @@ public enum Type { return marshaller; } + , B extends Builder> BuilderFactory + getBuilderFactory() { + return builderFactory; + } + FieldDescriptor getDescriptor() { return field; } } + interface BuilderFactory, B extends Builder> { + + B newBuilder(V value); + } + interface Builder, B extends Builder> { Type getType(); B mergeFrom(P other); - boolean isIndexed(); + boolean getIndexed(); - B setIndexed(boolean indexed); + B indexed(boolean indexed); Integer getMeaning(); - B setMeaning(Integer meaning); + B meaning(Integer meaning); - V getValue(); + V get(); - B setValue(V value); + B set(V value); P build(); } @@ -97,14 +108,14 @@ interface Marshaller, B extends Builder> } abstract static class BaseMarshaller, B extends Builder> - implements Marshaller { + implements Marshaller, BuilderFactory { @Override public final B fromProto(Value proto) { B builder = newBuilder(getValue(proto)); - builder.setIndexed(proto.getIndexed()); + builder.indexed(proto.getIndexed()); if (proto.hasMeaning()) { - builder.setMeaning(proto.getMeaning()); + builder.meaning(proto.getMeaning()); } return builder; } @@ -112,7 +123,7 @@ public final B fromProto(Value proto) { @Override public final Value toProto(P property) { Value.Builder builder = Value.newBuilder(); - builder.setIndexed(property.isIndexed()); + builder.setIndexed(property.getIndexed()); if (property.getMeaning() != null) { builder.setMeaning(property.getMeaning()); } @@ -120,9 +131,6 @@ public final Value toProto(P property) { return builder.build(); } - // Move to a Builder Factory - protected abstract B newBuilder(V value); - protected abstract V getValue(Value from); protected abstract void setValue(P from, Value.Builder to); @@ -147,19 +155,19 @@ public Type getType() { @Override public B mergeFrom(P other) { - indexed = other.isIndexed(); + indexed = other.getIndexed(); meaning = other.getMeaning(); - setValue(other.getValue()); + set(other.get()); return self(); } @Override - public boolean isIndexed() { + public boolean getIndexed() { return indexed; } @Override - public B setIndexed(boolean indexed) { + public B indexed(boolean indexed) { this.indexed = indexed; return self(); } @@ -170,23 +178,26 @@ public Integer getMeaning() { } @Override - public B setMeaning(Integer meaning) { + public B meaning(Integer meaning) { this.meaning = meaning; return self(); } @Override - public V getValue() { + public V get() { return value; } @Override - public B setValue(V value) { + public B set(V value) { this.value = checkNotNull(value); return self(); } - protected abstract B self(); + @SuppressWarnings("unchecked") + private B self() { + return (B) this; + } @Override public abstract P build(); @@ -194,7 +205,7 @@ public B setValue(V value) { Property(Builder builder) { type = builder.getType(); - indexed = builder.isIndexed(); + indexed = builder.getIndexed(); meaning = builder.getMeaning(); // some validations: if (meaning != null) { @@ -206,14 +217,14 @@ public B setValue(V value) { "Indexed values should not have meaning with 15 or 22"); } } - value = builder.getValue(); + value = builder.get(); } public final Type getType() { return type; } - public final boolean isIndexed() { + public final boolean getIndexed() { return indexed; } @@ -221,18 +232,19 @@ public final Integer getMeaning() { return meaning; } - public final V getValue() { + public final V get() { return value; } public final B toBuilder() { - Marshaller marshaller = getType().getMarshaller(); - return marshaller.fromProto(toPb()); + BuilderFactory builderFactory = getType().getBuilderFactory(); + B builder = builderFactory.newBuilder(get()); + return builder.mergeFrom((P) this); } @Override public int hashCode() { - return Objects.hash(getType(), isIndexed(), getMeaning()); + return Objects.hash(type, indexed, meaning); } @SuppressWarnings("unchecked") @@ -244,9 +256,9 @@ public boolean equals(Object other) { Property otherProperty = (Property) other; return Objects.equals(type, otherProperty.getType()) - && Objects.equals(indexed, otherProperty.isIndexed()) + && Objects.equals(indexed, otherProperty.getIndexed()) && Objects.equals(meaning, otherProperty.getMeaning()) - && Objects.equals(getValue(), otherProperty.getValue()); + && Objects.equals(value, otherProperty.get()); } @SuppressWarnings("unchecked") @@ -255,22 +267,31 @@ Value toPb() { return marshaller.toProto((P) this); } - @SuppressWarnings("unchecked") + @SuppressWarnings({ "rawtypes", "unchecked" }) static , B extends Property.Builder> Property fromPb(Value proto) { + Marshaller marshaller = NullProperty.MARSHALLER; for (Type type : Type.values()) { - if (proto.hasField(type.getDescriptor())) { - return (Property) type.getMarshaller().fromProto(proto).build(); + FieldDescriptor descriptor = type.getDescriptor(); + if (descriptor != null) { + if (descriptor.isRepeated()) { + if (proto.getRepeatedFieldCount(descriptor) > 0) { + marshaller = type.getMarshaller(); + break; + } + } else if (proto.hasField(descriptor)) { + marshaller = type.getMarshaller(); + break; + } } } // change strategy to return RawProperty (package scope constructor) // when no match instead of null. This could only be done - // when using the V1 API which added a NullValue type to distinct the cases + // when using the V3 API which added a NullValue type to distinct the cases // and the use of oneof which generates an enum of all possible values. - return (Property) new NullProperty(); + return marshaller.fromProto(proto).build(); } - private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(toPb().toByteArray()); @@ -283,7 +304,7 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE } @SuppressWarnings("unused") - private Object readResolve() throws ObjectStreamException { + protected Object readResolve() throws ObjectStreamException { return fromPb(tempValuePb); } } diff --git a/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java b/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java index 6bf9290e41e5..9bd63168a800 100644 --- a/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java +++ b/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java @@ -14,7 +14,7 @@ public final class PropertyListProperty extends private static final long serialVersionUID = -5461475706792576395L; - static final Marshaller>, PropertyListProperty, Builder> MARSHALLER = + static final BaseMarshaller>, PropertyListProperty, Builder> MARSHALLER = new BaseMarshaller>, PropertyListProperty, Builder>() { @Override @@ -23,8 +23,8 @@ public int getProtoFieldId() { } @Override - protected Builder newBuilder(List> properties) { - return new Builder().setValue(properties); + public Builder newBuilder(List> properties) { + return new Builder().set(properties); } @Override @@ -38,7 +38,7 @@ protected Builder newBuilder(List> properties) { @Override protected void setValue(PropertyListProperty from, Value.Builder to) { - for (Property property : from.getValue()) { + for (Property property : from.get()) { to.addListValue(property.toPb()); } } @@ -51,7 +51,7 @@ public static final class Builder extends public Builder() { super(Type.PROPERTY_LIST); - setIndexed(false); + indexed(false); } public Builder addProperty(Property property) { @@ -61,8 +61,18 @@ public Builder addProperty(Property property) { return this; } + public Builder addProperty(Property first, Property... other) { + addProperty(first); + for (Property property : other) { + Preconditions.checkArgument( + property.getType() != Type.PROPERTY_LIST, "Cannot contain another list"); + listBuilder.add(property); + } + return this; + } + @Override - public Builder setValue(List> properties) { + public Builder set(List> properties) { listBuilder = ImmutableList.>builder(); for (Property property : properties) { addProperty(property); @@ -71,24 +81,23 @@ public Builder setValue(List> properties) { } @Override - public List> getValue() { + public List> get() { return listBuilder.build(); } - @Override - protected Builder self() { - return this; - } - @Override public PropertyListProperty build() { - Preconditions.checkState(!getValue().isEmpty(), "Property list could not be empty"); + Preconditions.checkState(!get().isEmpty(), "Property list could not be empty"); return new PropertyListProperty(this); } } public PropertyListProperty(List> properties) { - this(new Builder().setValue(properties)); + this(new Builder().set(properties)); + } + + public PropertyListProperty(Property first, Property... other) { + this(new Builder().addProperty(first, other)); } PropertyListProperty(Builder builder) { diff --git a/src/main/java/com/google/gcloud/datastore/StringProperty.java b/src/main/java/com/google/gcloud/datastore/StringProperty.java index 319d57144a50..1b021cba0ef7 100644 --- a/src/main/java/com/google/gcloud/datastore/StringProperty.java +++ b/src/main/java/com/google/gcloud/datastore/StringProperty.java @@ -9,7 +9,7 @@ public final class StringProperty extends Property MARSHALLER = + static final BaseMarshaller MARSHALLER = new BaseMarshaller() { @Override @@ -18,7 +18,7 @@ public int getProtoFieldId() { } @Override - protected Builder newBuilder(String value) { + public Builder newBuilder(String value) { return new Builder(value); } @@ -29,7 +29,7 @@ protected String getValue(Value from) { @Override protected void setValue(StringProperty from, Value.Builder to) { - to.setStringValue(from.getValue()); + to.setStringValue(from.get()); } }; @@ -37,21 +37,16 @@ public static final class Builder extends Property.BaseBuilder typeToProperties = ImmutableMap.of( + Type.NULL, array(NULL_PROPERTY), + Type.KEY, array(KEY_PROPERTY), + Type.STRING, array(STRING_PROPERTY), + Type.EMBEDDED_ENTITY, array(EMBEDDED_ENTITY_PROPERTY1, EMBEDDED_ENTITY_PROPERTY2, + EMBEDDED_ENTITY_PROPERTY3), + Type.PROPERTY_LIST, array(PROPERTY_LIST_PROPERTY)); + + private static T[] array(T... values) { + return values; + } + + @Test + public void testProperties() throws Exception { + for (Type type : Type.values()) { + Property[] properties = typeToProperties.get(type); + for (Property property : properties) { + Property copy = serialiazeAndDeserialize(property); + assertEquals(property, copy); + assertEquals(property.get(), copy.get()); + } + } + } + + @Test + public void testTypes() throws Exception { + Object[] types = new Object[] { KEY1, KEY2, INCOMPLETE_KEY, ENTITY1, ENTITY2, EMBEDDED_ENTITY1, + EMBEDDED_ENTITY2, EMBEDDED_ENTITY3}; + for (Object obj : types) { + Object copy = serialiazeAndDeserialize(obj); + assertEquals(obj, copy); + } + } + + private T serialiazeAndDeserialize(T obj) throws Exception { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bOut); + out.writeObject(obj); + out.close(); + byte[] bytes = bOut.toByteArray(); + ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); + ObjectInputStream in = new ObjectInputStream(bIn); + @SuppressWarnings("unchecked") + T copy = (T) in.readObject(); + in.close(); + return copy; + } +} From 4a7434884b5bff969c1c245c258bf4c6d26ee93e Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 26 Nov 2014 17:55:15 -0800 Subject: [PATCH 020/771] import change --- .../google/gcloud/datastore/KeyProperty.java | 6 ++--- .../google/gcloud/datastore/NullProperty.java | 6 ++--- .../com/google/gcloud/datastore/Property.java | 27 ++++++++++--------- .../datastore/PropertyListProperty.java | 8 +++--- .../gcloud/datastore/StringProperty.java | 6 ++--- 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/KeyProperty.java b/src/main/java/com/google/gcloud/datastore/KeyProperty.java index fda5daa11dcb..3dc8debcbc79 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyProperty.java +++ b/src/main/java/com/google/gcloud/datastore/KeyProperty.java @@ -2,7 +2,7 @@ import static com.google.api.services.datastore.DatastoreV1.Value.KEY_VALUE_FIELD_NUMBER; -import com.google.api.services.datastore.DatastoreV1.Value; +import com.google.api.services.datastore.DatastoreV1; public final class KeyProperty extends Property { @@ -22,12 +22,12 @@ public Builder newBuilder(Key key) { } @Override - protected Key getValue(Value from) { + protected Key getValue(DatastoreV1.Value from) { return Key.fromPb(from.getKeyValue()); } @Override - protected void setValue(KeyProperty from, Value.Builder to) { + protected void setValue(KeyProperty from, DatastoreV1.Value.Builder to) { to.setKeyValue(from.get().toPb()); } }; diff --git a/src/main/java/com/google/gcloud/datastore/NullProperty.java b/src/main/java/com/google/gcloud/datastore/NullProperty.java index daaaf29899cb..3ac63bd785eb 100644 --- a/src/main/java/com/google/gcloud/datastore/NullProperty.java +++ b/src/main/java/com/google/gcloud/datastore/NullProperty.java @@ -2,7 +2,7 @@ import static com.google.common.base.Preconditions.checkArgument; -import com.google.api.services.datastore.DatastoreV1.Value; +import com.google.api.services.datastore.DatastoreV1; public final class NullProperty extends Property { @@ -22,12 +22,12 @@ public int getProtoFieldId() { } @Override - protected Void getValue(Value from) { + protected Void getValue(DatastoreV1.Value from) { return null; } @Override - protected void setValue(NullProperty from, Value.Builder to) { + protected void setValue(NullProperty from, DatastoreV1.Value.Builder to) { // nothing to set } }; diff --git a/src/main/java/com/google/gcloud/datastore/Property.java b/src/main/java/com/google/gcloud/datastore/Property.java index 50ee7ca79834..f2d3af575891 100644 --- a/src/main/java/com/google/gcloud/datastore/Property.java +++ b/src/main/java/com/google/gcloud/datastore/Property.java @@ -3,7 +3,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.api.services.datastore.DatastoreV1.Value; +import com.google.api.services.datastore.DatastoreV1; import com.google.protobuf.Descriptors.FieldDescriptor; import java.io.IOException; @@ -24,7 +24,7 @@ private final transient boolean indexed; private final transient Integer meaning; private final transient V value; - private transient Value tempValuePb; // only for deserialization + private transient DatastoreV1.Value tempValuePb; // only for deserialization public enum Type { @@ -54,7 +54,7 @@ public enum Type { Type(Marshaller marshaller, BuilderFactory builderFactory) { this.marshaller = marshaller; this.builderFactory = builderFactory; - field = Value.getDescriptor().findFieldByNumber(marshaller.getProtoFieldId()); + field = DatastoreV1.Value.getDescriptor().findFieldByNumber(marshaller.getProtoFieldId()); } , B extends Builder> Marshaller @@ -100,9 +100,9 @@ interface Builder, B extends Builder> { interface Marshaller, B extends Builder> { - B fromProto(Value proto); + B fromProto(DatastoreV1.Value proto); - Value toProto(P property); + DatastoreV1.Value toProto(P property); int getProtoFieldId(); } @@ -111,7 +111,7 @@ abstract static class BaseMarshaller, B extends B implements Marshaller, BuilderFactory { @Override - public final B fromProto(Value proto) { + public final B fromProto(DatastoreV1.Value proto) { B builder = newBuilder(getValue(proto)); builder.indexed(proto.getIndexed()); if (proto.hasMeaning()) { @@ -121,8 +121,8 @@ public final B fromProto(Value proto) { } @Override - public final Value toProto(P property) { - Value.Builder builder = Value.newBuilder(); + public final DatastoreV1.Value toProto(P property) { + DatastoreV1.Value.Builder builder = DatastoreV1.Value.newBuilder(); builder.setIndexed(property.getIndexed()); if (property.getMeaning() != null) { builder.setMeaning(property.getMeaning()); @@ -131,9 +131,9 @@ public final Value toProto(P property) { return builder.build(); } - protected abstract V getValue(Value from); + protected abstract V getValue(DatastoreV1.Value from); - protected abstract void setValue(P from, Value.Builder to); + protected abstract void setValue(P from, DatastoreV1.Value.Builder to); } abstract static class BaseBuilder, B extends BaseBuilder> @@ -236,6 +236,7 @@ public final V get() { return value; } + @SuppressWarnings("unchecked") public final B toBuilder() { BuilderFactory builderFactory = getType().getBuilderFactory(); B builder = builderFactory.newBuilder(get()); @@ -262,14 +263,14 @@ public boolean equals(Object other) { } @SuppressWarnings("unchecked") - Value toPb() { + DatastoreV1.Value toPb() { Marshaller marshaller = getType().getMarshaller(); return marshaller.toProto((P) this); } @SuppressWarnings({ "rawtypes", "unchecked" }) static , B extends Property.Builder> Property - fromPb(Value proto) { + fromPb(DatastoreV1.Value proto) { Marshaller marshaller = NullProperty.MARSHALLER; for (Type type : Type.values()) { FieldDescriptor descriptor = type.getDescriptor(); @@ -300,7 +301,7 @@ private void writeObject(ObjectOutputStream out) throws IOException { private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); byte[] bytes = (byte[]) in.readObject(); - tempValuePb = Value.parseFrom(bytes); + tempValuePb = DatastoreV1.Value.parseFrom(bytes); } @SuppressWarnings("unused") diff --git a/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java b/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java index 9bd63168a800..5c48e50188f4 100644 --- a/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java +++ b/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java @@ -2,7 +2,7 @@ import static com.google.api.services.datastore.DatastoreV1.Value.LIST_VALUE_FIELD_NUMBER; -import com.google.api.services.datastore.DatastoreV1.Value; +import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -28,16 +28,16 @@ public Builder newBuilder(List> properties) { } @Override - protected List> getValue(Value from) { + protected List> getValue(DatastoreV1.Value from) { List> properties = new ArrayList<>(from.getListValueCount()); - for (Value valuePb : from.getListValueList()) { + for (DatastoreV1.Value valuePb : from.getListValueList()) { properties.add(Property.fromPb(valuePb)); } return properties; } @Override - protected void setValue(PropertyListProperty from, Value.Builder to) { + protected void setValue(PropertyListProperty from, DatastoreV1.Value.Builder to) { for (Property property : from.get()) { to.addListValue(property.toPb()); } diff --git a/src/main/java/com/google/gcloud/datastore/StringProperty.java b/src/main/java/com/google/gcloud/datastore/StringProperty.java index 1b021cba0ef7..997c27cfb877 100644 --- a/src/main/java/com/google/gcloud/datastore/StringProperty.java +++ b/src/main/java/com/google/gcloud/datastore/StringProperty.java @@ -3,7 +3,7 @@ import static com.google.api.services.datastore.DatastoreV1.Value.STRING_VALUE_FIELD_NUMBER; import static com.google.common.base.Preconditions.checkArgument; -import com.google.api.services.datastore.DatastoreV1.Value; +import com.google.api.services.datastore.DatastoreV1; public final class StringProperty extends Property { @@ -23,12 +23,12 @@ public Builder newBuilder(String value) { } @Override - protected String getValue(Value from) { + protected String getValue(DatastoreV1.Value from) { return from.getStringValue(); } @Override - protected void setValue(StringProperty from, Value.Builder to) { + protected void setValue(StringProperty from, DatastoreV1.Value.Builder to) { to.setStringValue(from.get()); } }; From 221448b83dc167ce992fe7941873069ccf92e190 Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 26 Nov 2014 18:15:54 -0800 Subject: [PATCH 021/771] rename property to value --- .classpath | 14 ++- .project | 4 +- .settings/org.eclipse.core.resources.prefs | 3 + .settings/org.eclipse.jdt.core.prefs | 4 + .../gcloud/datastore/EmbeddedEntity.java | 14 +-- ...Property.java => EmbeddedEntityValue.java} | 20 ++-- .../com/google/gcloud/datastore/Entity.java | 16 +-- .../{KeyProperty.java => KeyValue.java} | 18 +-- .../google/gcloud/datastore/ListValue.java | 103 +++++++++++++++++ .../{NullProperty.java => NullValue.java} | 18 +-- .../datastore/PropertyListProperty.java | 106 ------------------ .../{StringProperty.java => StringValue.java} | 18 +-- .../datastore/{Property.java => Value.java} | 58 +++++----- .../gcloud/datastore/SerializationTest.java | 52 ++++----- 14 files changed, 229 insertions(+), 219 deletions(-) create mode 100644 .settings/org.eclipse.core.resources.prefs rename src/main/java/com/google/gcloud/datastore/{EmbeddedEntityProperty.java => EmbeddedEntityValue.java} (58%) rename src/main/java/com/google/gcloud/datastore/{KeyProperty.java => KeyValue.java} (60%) create mode 100644 src/main/java/com/google/gcloud/datastore/ListValue.java rename src/main/java/com/google/gcloud/datastore/{NullProperty.java => NullValue.java} (61%) delete mode 100644 src/main/java/com/google/gcloud/datastore/PropertyListProperty.java rename src/main/java/com/google/gcloud/datastore/{StringProperty.java => StringValue.java} (63%) rename src/main/java/com/google/gcloud/datastore/{Property.java => Value.java} (78%) diff --git a/.classpath b/.classpath index 09fe957d54f4..d6cf6121af66 100644 --- a/.classpath +++ b/.classpath @@ -21,11 +21,17 @@ - - - - + + + + + + + + + + diff --git a/.project b/.project index 3b20f35b11e3..fac3e5997ca3 100644 --- a/.project +++ b/.project @@ -16,7 +16,7 @@ - org.eclipse.m2e.core.maven2Builder + edu.umd.cs.findbugs.plugin.eclipse.findbugsBuilder @@ -26,7 +26,7 @@ - edu.umd.cs.findbugs.plugin.eclipse.findbugsBuilder + org.eclipse.m2e.core.maven2Builder diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000000..5b781ec6d952 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/test/java=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 0ba1de1e6e8a..fe5e054849a5 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -2,3 +2,7 @@ <<<<<<<=HEAD >>>>>>>=ef72daa81c73f99e2156f5bfe8127591fc6358e9 eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java b/src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java index 853d4edf5d55..957c57ce9048 100644 --- a/src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java +++ b/src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java @@ -18,13 +18,13 @@ public final class EmbeddedEntity implements Serializable { private static final long serialVersionUID = 6492561268709192891L; private final transient IncompleteKey key; - private final transient ImmutableSortedMap> properties; + private final transient ImmutableSortedMap> properties; private transient DatastoreV1.Entity tempEntityPb; // only for deserialization public static final class Builder { private IncompleteKey key; - private Map> properties = new HashMap<>(); + private Map> properties = new HashMap<>(); public Builder() { } @@ -54,8 +54,8 @@ public Builder removeProperty(String name) { return this; } - public Builder setProperty(String name, Property property) { - properties.put(name, property); + public Builder setProperty(String name, Value value) { + properties.put(name, value); return this; } @@ -85,7 +85,7 @@ public boolean hasProperty(String name) { return properties.containsKey(name); } - public Property getProperty(String name) { + public Value getProperty(String name) { return properties.get(name); } @@ -119,7 +119,7 @@ static EmbeddedEntity fromPb(DatastoreV1.Entity entityPb) { builder.setKey(IncompleteKey.fromPb(entityPb.getKey())); } for (DatastoreV1.Property property : entityPb.getPropertyList()) { - builder.setProperty(property.getName(), Property.fromPb(property.getValue())); + builder.setProperty(property.getName(), Value.fromPb(property.getValue())); } return builder.build(); } @@ -129,7 +129,7 @@ DatastoreV1.Entity toPb() { if (key != null) { entityPb.setKey(key.toPb()); } - for (Map.Entry> entry : properties.entrySet()) { + for (Map.Entry> entry : properties.entrySet()) { DatastoreV1.Property.Builder propertyPb = DatastoreV1.Property.newBuilder(); propertyPb.setName(entry.getKey()); propertyPb.setValue(entry.getValue().toPb()); diff --git a/src/main/java/com/google/gcloud/datastore/EmbeddedEntityProperty.java b/src/main/java/com/google/gcloud/datastore/EmbeddedEntityValue.java similarity index 58% rename from src/main/java/com/google/gcloud/datastore/EmbeddedEntityProperty.java rename to src/main/java/com/google/gcloud/datastore/EmbeddedEntityValue.java index d1d653ac9f07..d63313c6e9ff 100644 --- a/src/main/java/com/google/gcloud/datastore/EmbeddedEntityProperty.java +++ b/src/main/java/com/google/gcloud/datastore/EmbeddedEntityValue.java @@ -4,13 +4,13 @@ import com.google.api.services.datastore.DatastoreV1; -public final class EmbeddedEntityProperty extends - Property { +public final class EmbeddedEntityValue extends + Value { private static final long serialVersionUID = -5461475706792576395L; - static final BaseMarshaller MARSHALLER = - new BaseMarshaller() { + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { @Override public int getProtoFieldId() { @@ -28,13 +28,13 @@ protected EmbeddedEntity getValue(DatastoreV1.Value from) { } @Override - protected void setValue(EmbeddedEntityProperty from, DatastoreV1.Value.Builder to) { + protected void setValue(EmbeddedEntityValue from, DatastoreV1.Value.Builder to) { to.setEntityValue(from.get().toPb()); } }; public static final class Builder extends - Property.BaseBuilder { + Value.BaseBuilder { public Builder(EmbeddedEntity entity) { super(Type.EMBEDDED_ENTITY); @@ -43,16 +43,16 @@ public Builder(EmbeddedEntity entity) { } @Override - public EmbeddedEntityProperty build() { - return new EmbeddedEntityProperty(this); + public EmbeddedEntityValue build() { + return new EmbeddedEntityValue(this); } } - public EmbeddedEntityProperty(EmbeddedEntity entity) { + public EmbeddedEntityValue(EmbeddedEntity entity) { this(new Builder(entity)); } - EmbeddedEntityProperty(Builder builder) { + EmbeddedEntityValue(Builder builder) { super(builder); } } diff --git a/src/main/java/com/google/gcloud/datastore/Entity.java b/src/main/java/com/google/gcloud/datastore/Entity.java index 84f42b434ced..2d260b7d19b1 100644 --- a/src/main/java/com/google/gcloud/datastore/Entity.java +++ b/src/main/java/com/google/gcloud/datastore/Entity.java @@ -21,13 +21,13 @@ public final class Entity implements Serializable { private static final long serialVersionUID = 432961565733066915L; private final transient Key key; - private final transient ImmutableSortedMap> properties; + private final transient ImmutableSortedMap> properties; private transient DatastoreV1.Entity tempEntityPb; // only for deserialization public static final class Builder { private Key key; - private Map> properties; + private Map> properties; public Builder(Key key) { this.key = checkNotNull(key); @@ -49,8 +49,8 @@ public Builder removeProperty(String name) { return this; } - public Builder setProperty(String name, Property property) { - properties.put(name, property); + public Builder setProperty(String name, Value value) { + properties.put(name, value); return this; } @@ -72,7 +72,7 @@ public boolean hasProperty(String name) { return properties.containsKey(name); } - public Property getProperty(String name) { + public Value getProperty(String name) { return properties.get(name); } @@ -80,7 +80,7 @@ public Set getPropertyNames() { return properties.keySet(); } - ImmutableSortedMap> getProperties() { + ImmutableSortedMap> getProperties() { return properties; } @@ -108,7 +108,7 @@ static Entity fromPb(DatastoreV1.Entity entityPb) { Preconditions.checkArgument(entityPb.hasKey()); Builder builder = new Builder(Key.fromPb(entityPb.getKey())); for (DatastoreV1.Property property : entityPb.getPropertyList()) { - builder.setProperty(property.getName(), Property.fromPb(property.getValue())); + builder.setProperty(property.getName(), Value.fromPb(property.getValue())); } return builder.build(); } @@ -116,7 +116,7 @@ static Entity fromPb(DatastoreV1.Entity entityPb) { DatastoreV1.Entity toPb() { DatastoreV1.Entity.Builder entityPb = DatastoreV1.Entity.newBuilder(); entityPb.setKey(key.toPb()); - for (Map.Entry> entry : properties.entrySet()) { + for (Map.Entry> entry : properties.entrySet()) { DatastoreV1.Property.Builder propertyPb = DatastoreV1.Property.newBuilder(); propertyPb.setName(entry.getKey()); propertyPb.setValue(entry.getValue().toPb()); diff --git a/src/main/java/com/google/gcloud/datastore/KeyProperty.java b/src/main/java/com/google/gcloud/datastore/KeyValue.java similarity index 60% rename from src/main/java/com/google/gcloud/datastore/KeyProperty.java rename to src/main/java/com/google/gcloud/datastore/KeyValue.java index 3dc8debcbc79..637fb6931bdb 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyProperty.java +++ b/src/main/java/com/google/gcloud/datastore/KeyValue.java @@ -4,12 +4,12 @@ import com.google.api.services.datastore.DatastoreV1; -public final class KeyProperty extends Property { +public final class KeyValue extends Value { private static final long serialVersionUID = -1318353707326704821L; - static final BaseMarshaller MARSHALLER = - new BaseMarshaller() { + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { @Override public int getProtoFieldId() { @@ -27,12 +27,12 @@ protected Key getValue(DatastoreV1.Value from) { } @Override - protected void setValue(KeyProperty from, DatastoreV1.Value.Builder to) { + protected void setValue(KeyValue from, DatastoreV1.Value.Builder to) { to.setKeyValue(from.get().toPb()); } }; - public static final class Builder extends Property.BaseBuilder { + public static final class Builder extends Value.BaseBuilder { public Builder(Key value) { super(Type.KEY); @@ -40,16 +40,16 @@ public Builder(Key value) { } @Override - public KeyProperty build() { - return new KeyProperty(this); + public KeyValue build() { + return new KeyValue(this); } } - public KeyProperty(Key key) { + public KeyValue(Key key) { this(new Builder(key)); } - KeyProperty(Builder builder) { + KeyValue(Builder builder) { super(builder); } } diff --git a/src/main/java/com/google/gcloud/datastore/ListValue.java b/src/main/java/com/google/gcloud/datastore/ListValue.java new file mode 100644 index 000000000000..b33c096ff718 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/ListValue.java @@ -0,0 +1,103 @@ +package com.google.gcloud.datastore; + +import static com.google.api.services.datastore.DatastoreV1.Value.LIST_VALUE_FIELD_NUMBER; + +import com.google.api.services.datastore.DatastoreV1; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.ArrayList; +import java.util.List; + +public final class ListValue extends + Value>, ListValue, ListValue.Builder> { + + private static final long serialVersionUID = -5461475706792576395L; + + static final BaseMarshaller>, ListValue, Builder> MARSHALLER = + new BaseMarshaller>, ListValue, Builder>() { + + @Override + public int getProtoFieldId() { + return LIST_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(List> values) { + return new Builder().set(values); + } + + @Override + protected List> getValue(DatastoreV1.Value from) { + List> properties = new ArrayList<>(from.getListValueCount()); + for (DatastoreV1.Value valuePb : from.getListValueList()) { + properties.add(Value.fromPb(valuePb)); + } + return properties; + } + + @Override + protected void setValue(ListValue from, DatastoreV1.Value.Builder to) { + for (Value property : from.get()) { + to.addListValue(property.toPb()); + } + } + }; + + public static final class Builder extends + Value.BaseBuilder>, ListValue, Builder> { + + private ImmutableList.Builder> listBuilder = ImmutableList.builder(); + + public Builder() { + super(Type.LIST); + indexed(false); + } + + public Builder addValue(Value value) { + Preconditions.checkArgument(value.getType() != Type.LIST, "Cannot contain another list"); + listBuilder.add(value); + return this; + } + + public Builder addValue(Value first, Value... other) { + addValue(first); + for (Value value : other) { + addValue(value); + } + return this; + } + + @Override + public Builder set(List> properties) { + listBuilder = ImmutableList.>builder(); + for (Value property : properties) { + addValue(property); + } + return this; + } + + @Override + public List> get() { + return listBuilder.build(); + } + + @Override + public ListValue build() { + Preconditions.checkState(!get().isEmpty(), "value list could not be empty"); + return new ListValue(this); + } + } + + public ListValue(List> properties) { + this(new Builder().set(properties)); + } + + public ListValue(Value first, Value... other) { + this(new Builder().addValue(first, other)); + } + + ListValue(Builder builder) { + super(builder); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/NullProperty.java b/src/main/java/com/google/gcloud/datastore/NullValue.java similarity index 61% rename from src/main/java/com/google/gcloud/datastore/NullProperty.java rename to src/main/java/com/google/gcloud/datastore/NullValue.java index 3ac63bd785eb..5d77c333a54f 100644 --- a/src/main/java/com/google/gcloud/datastore/NullProperty.java +++ b/src/main/java/com/google/gcloud/datastore/NullValue.java @@ -4,12 +4,12 @@ import com.google.api.services.datastore.DatastoreV1; -public final class NullProperty extends Property { +public final class NullValue extends Value { private static final long serialVersionUID = 8497300779013002270L; - static final BaseMarshaller MARSHALLER = - new BaseMarshaller() { + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { @Override public Builder newBuilder(Void value) { @@ -27,20 +27,20 @@ protected Void getValue(DatastoreV1.Value from) { } @Override - protected void setValue(NullProperty from, DatastoreV1.Value.Builder to) { + protected void setValue(NullValue from, DatastoreV1.Value.Builder to) { // nothing to set } }; - public static final class Builder extends Property.BaseBuilder { + public static final class Builder extends Value.BaseBuilder { public Builder() { super(Type.NULL); } @Override - public NullProperty build() { - return new NullProperty(this); + public NullValue build() { + return new NullValue(this); } @Override @@ -50,11 +50,11 @@ public Builder set(Void value) { } } - public NullProperty() { + public NullValue() { this(new Builder().indexed(true)); } - NullProperty(Builder builder) { + NullValue(Builder builder) { super(builder); } } diff --git a/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java b/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java deleted file mode 100644 index 5c48e50188f4..000000000000 --- a/src/main/java/com/google/gcloud/datastore/PropertyListProperty.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.google.gcloud.datastore; - -import static com.google.api.services.datastore.DatastoreV1.Value.LIST_VALUE_FIELD_NUMBER; - -import com.google.api.services.datastore.DatastoreV1; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; - -import java.util.ArrayList; -import java.util.List; - -public final class PropertyListProperty extends - Property>, PropertyListProperty, PropertyListProperty.Builder> { - - private static final long serialVersionUID = -5461475706792576395L; - - static final BaseMarshaller>, PropertyListProperty, Builder> MARSHALLER = - new BaseMarshaller>, PropertyListProperty, Builder>() { - - @Override - public int getProtoFieldId() { - return LIST_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(List> properties) { - return new Builder().set(properties); - } - - @Override - protected List> getValue(DatastoreV1.Value from) { - List> properties = new ArrayList<>(from.getListValueCount()); - for (DatastoreV1.Value valuePb : from.getListValueList()) { - properties.add(Property.fromPb(valuePb)); - } - return properties; - } - - @Override - protected void setValue(PropertyListProperty from, DatastoreV1.Value.Builder to) { - for (Property property : from.get()) { - to.addListValue(property.toPb()); - } - } - }; - - public static final class Builder extends - Property.BaseBuilder>, PropertyListProperty, Builder> { - - private ImmutableList.Builder> listBuilder = ImmutableList.builder(); - - public Builder() { - super(Type.PROPERTY_LIST); - indexed(false); - } - - public Builder addProperty(Property property) { - Preconditions.checkArgument( - property.getType() != Type.PROPERTY_LIST, "Cannot contain another list"); - listBuilder.add(property); - return this; - } - - public Builder addProperty(Property first, Property... other) { - addProperty(first); - for (Property property : other) { - Preconditions.checkArgument( - property.getType() != Type.PROPERTY_LIST, "Cannot contain another list"); - listBuilder.add(property); - } - return this; - } - - @Override - public Builder set(List> properties) { - listBuilder = ImmutableList.>builder(); - for (Property property : properties) { - addProperty(property); - } - return this; - } - - @Override - public List> get() { - return listBuilder.build(); - } - - @Override - public PropertyListProperty build() { - Preconditions.checkState(!get().isEmpty(), "Property list could not be empty"); - return new PropertyListProperty(this); - } - } - - public PropertyListProperty(List> properties) { - this(new Builder().set(properties)); - } - - public PropertyListProperty(Property first, Property... other) { - this(new Builder().addProperty(first, other)); - } - - PropertyListProperty(Builder builder) { - super(builder); - } -} diff --git a/src/main/java/com/google/gcloud/datastore/StringProperty.java b/src/main/java/com/google/gcloud/datastore/StringValue.java similarity index 63% rename from src/main/java/com/google/gcloud/datastore/StringProperty.java rename to src/main/java/com/google/gcloud/datastore/StringValue.java index 997c27cfb877..70d77e250782 100644 --- a/src/main/java/com/google/gcloud/datastore/StringProperty.java +++ b/src/main/java/com/google/gcloud/datastore/StringValue.java @@ -5,12 +5,12 @@ import com.google.api.services.datastore.DatastoreV1; -public final class StringProperty extends Property { +public final class StringValue extends Value { private static final long serialVersionUID = -3105699707394545523L; - static final BaseMarshaller MARSHALLER = - new BaseMarshaller() { + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { @Override public int getProtoFieldId() { @@ -28,12 +28,12 @@ protected String getValue(DatastoreV1.Value from) { } @Override - protected void setValue(StringProperty from, DatastoreV1.Value.Builder to) { + protected void setValue(StringValue from, DatastoreV1.Value.Builder to) { to.setStringValue(from.get()); } }; - public static final class Builder extends Property.BaseBuilder { + public static final class Builder extends Value.BaseBuilder { public Builder(String value) { super(Type.STRING); @@ -41,19 +41,19 @@ public Builder(String value) { } @Override - public StringProperty build() { + public StringValue build() { if (getIndexed()) { checkArgument(get().length() <= 500, "Indexed string is limited to 500 characters"); } - return new StringProperty(this); + return new StringValue(this); } } - public StringProperty(String content) { + public StringValue(String content) { this(new Builder(content)); } - StringProperty(Builder builder) { + StringValue(Builder builder) { super(builder); } } diff --git a/src/main/java/com/google/gcloud/datastore/Property.java b/src/main/java/com/google/gcloud/datastore/Value.java similarity index 78% rename from src/main/java/com/google/gcloud/datastore/Property.java rename to src/main/java/com/google/gcloud/datastore/Value.java index f2d3af575891..2a685eaf5acd 100644 --- a/src/main/java/com/google/gcloud/datastore/Property.java +++ b/src/main/java/com/google/gcloud/datastore/Value.java @@ -15,7 +15,7 @@ // TODO: add javadoc, and mention that null should only be represented by NullValue. public abstract class - Property, B extends Property.Builder> + Value, B extends Value.Builder> implements Serializable { private static final long serialVersionUID = -1899638277588872742L; @@ -28,11 +28,11 @@ public enum Type { - NULL(NullProperty.MARSHALLER, NullProperty.MARSHALLER), - STRING(StringProperty.MARSHALLER, StringProperty.MARSHALLER), - EMBEDDED_ENTITY(EmbeddedEntityProperty.MARSHALLER, EmbeddedEntityProperty.MARSHALLER), - PROPERTY_LIST(PropertyListProperty.MARSHALLER, PropertyListProperty.MARSHALLER), - KEY(KeyProperty.MARSHALLER, KeyProperty.MARSHALLER); + NULL(NullValue.MARSHALLER, NullValue.MARSHALLER), + STRING(StringValue.MARSHALLER, StringValue.MARSHALLER), + EMBEDDED_ENTITY(EmbeddedEntityValue.MARSHALLER, EmbeddedEntityValue.MARSHALLER), + LIST(ListValue.MARSHALLER, ListValue.MARSHALLER), + KEY(KeyValue.MARSHALLER, KeyValue.MARSHALLER); /* TODO: Also implement @@ -50,19 +50,19 @@ public enum Type { @SuppressWarnings("rawtypes") private final Marshaller marshaller; private FieldDescriptor field; - , B extends Builder> + , B extends Builder> Type(Marshaller marshaller, BuilderFactory builderFactory) { this.marshaller = marshaller; this.builderFactory = builderFactory; field = DatastoreV1.Value.getDescriptor().findFieldByNumber(marshaller.getProtoFieldId()); } - , B extends Builder> Marshaller + , B extends Builder> Marshaller getMarshaller() { return marshaller; } - , B extends Builder> BuilderFactory + , B extends Builder> BuilderFactory getBuilderFactory() { return builderFactory; } @@ -72,12 +72,12 @@ FieldDescriptor getDescriptor() { } } - interface BuilderFactory, B extends Builder> { + interface BuilderFactory, B extends Builder> { B newBuilder(V value); } - interface Builder, B extends Builder> { + interface Builder, B extends Builder> { Type getType(); @@ -98,16 +98,16 @@ interface Builder, B extends Builder> { P build(); } - interface Marshaller, B extends Builder> { + interface Marshaller, B extends Builder> { B fromProto(DatastoreV1.Value proto); - DatastoreV1.Value toProto(P property); + DatastoreV1.Value toProto(P value); int getProtoFieldId(); } - abstract static class BaseMarshaller, B extends Builder> + abstract static class BaseMarshaller, B extends Builder> implements Marshaller, BuilderFactory { @Override @@ -121,13 +121,13 @@ public final B fromProto(DatastoreV1.Value proto) { } @Override - public final DatastoreV1.Value toProto(P property) { + public final DatastoreV1.Value toProto(P value) { DatastoreV1.Value.Builder builder = DatastoreV1.Value.newBuilder(); - builder.setIndexed(property.getIndexed()); - if (property.getMeaning() != null) { - builder.setMeaning(property.getMeaning()); + builder.setIndexed(value.getIndexed()); + if (value.getMeaning() != null) { + builder.setMeaning(value.getMeaning()); } - setValue(property, builder); + setValue(value, builder); return builder.build(); } @@ -136,7 +136,7 @@ public final DatastoreV1.Value toProto(P property) { protected abstract void setValue(P from, DatastoreV1.Value.Builder to); } - abstract static class BaseBuilder, B extends BaseBuilder> + abstract static class BaseBuilder, B extends BaseBuilder> implements Builder { private final Type type; @@ -203,7 +203,7 @@ private B self() { public abstract P build(); } - Property(Builder builder) { + Value(Builder builder) { type = builder.getType(); indexed = builder.getIndexed(); meaning = builder.getMeaning(); @@ -255,11 +255,11 @@ public boolean equals(Object other) { return false; } - Property otherProperty = (Property) other; - return Objects.equals(type, otherProperty.getType()) - && Objects.equals(indexed, otherProperty.getIndexed()) - && Objects.equals(meaning, otherProperty.getMeaning()) - && Objects.equals(value, otherProperty.get()); + Value otherValue = (Value) other; + return Objects.equals(type, otherValue.getType()) + && Objects.equals(indexed, otherValue.getIndexed()) + && Objects.equals(meaning, otherValue.getMeaning()) + && Objects.equals(value, otherValue.get()); } @SuppressWarnings("unchecked") @@ -269,9 +269,9 @@ DatastoreV1.Value toPb() { } @SuppressWarnings({ "rawtypes", "unchecked" }) - static , B extends Property.Builder> Property + static , B extends Value.Builder> Value fromPb(DatastoreV1.Value proto) { - Marshaller marshaller = NullProperty.MARSHALLER; + Marshaller marshaller = NullValue.MARSHALLER; for (Type type : Type.values()) { FieldDescriptor descriptor = type.getDescriptor(); if (descriptor != null) { @@ -286,7 +286,7 @@ DatastoreV1.Value toPb() { } } } - // change strategy to return RawProperty (package scope constructor) + // change strategy to return RawValue (package scope constructor) // when no match instead of null. This could only be done // when using the V3 API which added a NullValue type to distinct the cases // and the use of oneof which generates an enum of all possible values. diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 19971da7af6d..48a91eceb8e4 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -3,7 +3,7 @@ import static org.junit.Assert.assertEquals; import com.google.common.collect.ImmutableMap; -import com.google.gcloud.datastore.Property.Type; +import com.google.gcloud.datastore.Value.Type; import org.junit.Test; @@ -19,13 +19,13 @@ public class SerializationTest { new IncompleteKey.Builder("ds", "k").addToPath("p", 1).build(); private static final Key KEY1 = new Key.Builder("ds", "k", "n").build(); private static final Key KEY2 = new Key.Builder(INCOMPLETE_KEY, 2).build(); - private static final KeyProperty KEY_PROPERTY = new KeyProperty(KEY1); - private static final NullProperty NULL_PROPERTY = - new NullProperty.Builder().indexed(true).build(); - private static final StringProperty STRING_PROPERTY = new StringProperty("hello"); + private static final KeyValue KEY_PROPERTY = new KeyValue(KEY1); + private static final NullValue NULL_PROPERTY = + new NullValue.Builder().indexed(true).build(); + private static final StringValue STRING_PROPERTY = new StringValue("hello"); private static final Entity ENTITY1 = new Entity.Builder(KEY1).build(); private static final Entity ENTITY2 = - new Entity.Builder(KEY2).setProperty("null", new NullProperty()).build(); + new Entity.Builder(KEY2).setProperty("null", new NullValue()).build(); private static final EmbeddedEntity EMBEDDED_ENTITY1 = new EmbeddedEntity(ENTITY1); private static final EmbeddedEntity EMBEDDED_ENTITY2 = new EmbeddedEntity(ENTITY2); private static final EmbeddedEntity EMBEDDED_ENTITY3 = @@ -34,46 +34,46 @@ public class SerializationTest { .setProperty("p1", STRING_PROPERTY) .setProperty("p2", STRING_PROPERTY) .build(); - private static final EmbeddedEntityProperty EMBEDDED_ENTITY_PROPERTY1 = - new EmbeddedEntityProperty(EMBEDDED_ENTITY1); - private static final EmbeddedEntityProperty EMBEDDED_ENTITY_PROPERTY2 = - new EmbeddedEntityProperty(EMBEDDED_ENTITY2); - private static final EmbeddedEntityProperty EMBEDDED_ENTITY_PROPERTY3 = - new EmbeddedEntityProperty(EMBEDDED_ENTITY3); - private static final PropertyListProperty PROPERTY_LIST_PROPERTY = - new PropertyListProperty.Builder() - .addProperty(NULL_PROPERTY) - .addProperty(STRING_PROPERTY) - .addProperty(new NullProperty()) + private static final EmbeddedEntityValue EMBEDDED_ENTITY_PROPERTY1 = + new EmbeddedEntityValue(EMBEDDED_ENTITY1); + private static final EmbeddedEntityValue EMBEDDED_ENTITY_PROPERTY2 = + new EmbeddedEntityValue(EMBEDDED_ENTITY2); + private static final EmbeddedEntityValue EMBEDDED_ENTITY_PROPERTY3 = + new EmbeddedEntityValue(EMBEDDED_ENTITY3); + private static final ListValue PROPERTY_LIST_PROPERTY = + new ListValue.Builder() + .addValue(NULL_PROPERTY) + .addValue(STRING_PROPERTY) + .addValue(new NullValue()) .build(); - private Map typeToProperties = ImmutableMap.of( + private Map typeToValues = ImmutableMap.of( Type.NULL, array(NULL_PROPERTY), Type.KEY, array(KEY_PROPERTY), Type.STRING, array(STRING_PROPERTY), Type.EMBEDDED_ENTITY, array(EMBEDDED_ENTITY_PROPERTY1, EMBEDDED_ENTITY_PROPERTY2, EMBEDDED_ENTITY_PROPERTY3), - Type.PROPERTY_LIST, array(PROPERTY_LIST_PROPERTY)); + Type.LIST, array(PROPERTY_LIST_PROPERTY)); private static T[] array(T... values) { return values; } @Test - public void testProperties() throws Exception { + public void testValues() throws Exception { for (Type type : Type.values()) { - Property[] properties = typeToProperties.get(type); - for (Property property : properties) { - Property copy = serialiazeAndDeserialize(property); - assertEquals(property, copy); - assertEquals(property.get(), copy.get()); + Value[] values = typeToValues.get(type); + for (Value value : values) { + Value copy = serialiazeAndDeserialize(value); + assertEquals(value, copy); + assertEquals(value.get(), copy.get()); } } } @Test public void testTypes() throws Exception { - Object[] types = new Object[] { KEY1, KEY2, INCOMPLETE_KEY, ENTITY1, ENTITY2, EMBEDDED_ENTITY1, + Object[] types = { KEY1, KEY2, INCOMPLETE_KEY, ENTITY1, ENTITY2, EMBEDDED_ENTITY1, EMBEDDED_ENTITY2, EMBEDDED_ENTITY3}; for (Object obj : types) { Object copy = serialiazeAndDeserialize(obj); From 65a9f121fbe37a34b1f855e4505853dba26d7a1d Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 26 Nov 2014 18:20:17 -0800 Subject: [PATCH 022/771] bla --- .../java/com/google/gcloud/datastore/DatastoreService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index ca4c55033322..604f69b0620f 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -5,7 +5,8 @@ public interface DatastoreService extends DatastoreReader, DatastoreWriter { interface Query { - + // TODO + // consider 2 types of queries regualr and Gql } From aa237dcf201e5865535d68689092cb6e6b220326 Mon Sep 17 00:00:00 2001 From: aozarov Date: Mon, 1 Dec 2014 17:37:14 -0800 Subject: [PATCH 023/771] work in progress --- .../gcloud/datastore/BatchWriteOption.java | 31 +++++ .../google/gcloud/datastore/BatchWriter.java | 6 + .../gcloud/datastore/DatastoreService.java | 65 +++++------ .../datastore/DatastoreServiceException.java | 20 ++++ .../datastore/DatastoreServiceImpl.java | 13 ++- .../datastore/DatastoreServiceOptions.java | 13 +++ .../gcloud/datastore/EmbeddedEntityValue.java | 58 ---------- .../com/google/gcloud/datastore/Entity.java | 107 ++++-------------- .../java/com/google/gcloud/datastore/Key.java | 43 ++++--- .../google/gcloud/datastore/KeyBuilder.java | 40 +++++++ ...EmbeddedEntity.java => PartialEntity.java} | 53 ++++----- .../gcloud/datastore/PartialEntityValue.java | 58 ++++++++++ .../{IncompleteKey.java => PartialKey.java} | 18 +-- .../google/gcloud/datastore/Transaction.java | 8 ++ .../gcloud/datastore/TransactionOption.java | 59 ++++++++++ .../com/google/gcloud/datastore/Value.java | 2 +- .../datastore/DatastoreServiceTest.java | 69 +++++++++++ .../gcloud/datastore/SerializationTest.java | 26 ++--- 18 files changed, 433 insertions(+), 256 deletions(-) create mode 100644 src/main/java/com/google/gcloud/datastore/BatchWriteOption.java create mode 100644 src/main/java/com/google/gcloud/datastore/BatchWriter.java create mode 100644 src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java delete mode 100644 src/main/java/com/google/gcloud/datastore/EmbeddedEntityValue.java create mode 100644 src/main/java/com/google/gcloud/datastore/KeyBuilder.java rename src/main/java/com/google/gcloud/datastore/{EmbeddedEntity.java => PartialEntity.java} (71%) create mode 100644 src/main/java/com/google/gcloud/datastore/PartialEntityValue.java rename src/main/java/com/google/gcloud/datastore/{IncompleteKey.java => PartialKey.java} (94%) create mode 100644 src/main/java/com/google/gcloud/datastore/Transaction.java create mode 100644 src/main/java/com/google/gcloud/datastore/TransactionOption.java create mode 100644 src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java b/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java new file mode 100644 index 000000000000..cda7bf7be16f --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java @@ -0,0 +1,31 @@ +package com.google.gcloud.datastore; + +import java.io.Serializable; + +public abstract class BatchWriteOption implements Serializable { + + private static final long serialVersionUID = -3932758377282659839L; + + BatchWriteOption() { + // package protected + } + + public static final class ForceWrites extends BatchWriteOption { + + private static final long serialVersionUID = 2555054296046232799L; + + private final boolean force; + + public ForceWrites(boolean force) { + this.force = force; + } + + public boolean isForce() { + return force; + } + } + + public static ForceWrites forceWrites() { + return new ForceWrites(true); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriter.java b/src/main/java/com/google/gcloud/datastore/BatchWriter.java new file mode 100644 index 000000000000..9d2e74f01cb1 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/BatchWriter.java @@ -0,0 +1,6 @@ +package com.google.gcloud.datastore; + +public interface BatchWriter extends DatastoreWriter { + + void submit(); +} \ No newline at end of file diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index 604f69b0620f..88993173bbb8 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -4,47 +4,48 @@ public interface DatastoreService extends DatastoreReader, DatastoreWriter { - interface Query { - // TODO - // consider 2 types of queries regualr and Gql + enum QueryType { + PROJECTION, + FULL; } + /* + interface QueryResult { - public interface Transaction extends DatastoreReader, DatastoreWriter { - - void commit(); - - void rollback(); } - public interface TransactionOptions { - - enum IsolationLevel { - SERIALIZABLE, SNAPSHOT; - } - - - IsolationLevel getIsolationLevel(); - - boolean force(); - } - - public interface BatchWriter extends DatastoreWriter { - - void submit(); + // consider 2 types of queries regualr and Gql + interface Query { + QueryResult keysOnly(QueryType query); } +*/ - public interface BatchOptions { - } DatastoreServiceOptions getOptions(); - Transaction newTransaction(TransactionOptions transactionOptions); - - BatchWriter newBatchWriter(BatchOptions batchOptions); - - Key allocateId(IncompleteKey key); - + KeyBuilder newKeyBuilder(String kind); + + /** + * Returns a new Datastore transaction. + * + * @throws DatastoreServiceExcepiton upon failure + */ + Transaction newTransaction(TransactionOption... transactionOption); + + BatchWriter newBatchWriter(BatchWriteOption... batchWriteOption); + + /** + * Returns a key with a newly allocated id. + * + * @throws DatastoreServiceExcepiton upon failure + */ + Key allocateId(PartialKey key); + + /** + * Returns a list of keys using the allocated ids ordered by the input. + * + * @throws DatastoreServiceExcepiton upon failure + */ // results are returned using request order - Iterator allocateIds(IncompleteKey... key); + Iterator allocateIds(PartialKey... key); } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java new file mode 100644 index 000000000000..5b27cd519654 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java @@ -0,0 +1,20 @@ +package com.google.gcloud.datastore; + +public class DatastoreServiceException extends RuntimeException { + + private static final long serialVersionUID = 8170357898917041899L; + + private final boolean isTransient; + + public DatastoreServiceException(boolean isTransient, String msg, Exception cause) { + super(msg, cause); + this.isTransient = isTransient; + } + + /** + * @return {@code true} if this exception is transient and could be safely retried. + */ + public boolean isTransient() { + return isTransient; + } +} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index cc5ed9fa3434..cd5475c12bab 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -17,25 +17,25 @@ public DatastoreServiceOptions getOptions() { } @Override - public Transaction newTransaction(TransactionOptions transactionOptions) { + public Transaction newTransaction(TransactionOption... transactionOption) { // TODO Auto-generated method stub return null; } @Override - public BatchWriter newBatchWriter(BatchOptions batchOptions) { + public BatchWriter newBatchWriter(BatchWriteOption... batchWriteOption) { // TODO Auto-generated method stub return null; } @Override - public Key allocateId(IncompleteKey key) { + public Key allocateId(PartialKey key) { // TODO Auto-generated method stub return null; } @Override - public Iterator allocateIds(IncompleteKey... key) { + public Iterator allocateIds(PartialKey... key) { // TODO Auto-generated method stub return null; } @@ -75,4 +75,9 @@ public void delete(Key... key) { // TODO Auto-generated method stub } + + @Override + public KeyBuilder newKeyBuilder(String kind) { + return new KeyBuilder(kind, options.getDefaultNamespace(), kind); + } } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java index bf2a58978fc1..06377531b25a 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java @@ -7,6 +7,7 @@ import com.google.common.collect.ImmutableSet; import com.google.gcloud.ServiceOptions; +import java.lang.reflect.Method; import java.util.Set; import java.util.regex.Pattern; @@ -72,6 +73,18 @@ public String getDataset() { return dataset; } + public String getDefaultNamespace() { + // TODO(ozarov): An alternative to reflection would be to depend on AE api jar: + // http://mvnrepository.com/artifact/com.google.appengine/appengine-api-1.0-sdk/1.2.0 + try { + Class clazz = Class.forName("com.google.appengine.api.NamespaceManager"); + Method method = clazz.getMethod("get"); + return (String) method.invoke(null); + } catch (Exception ex) { + return ""; + } + } + public boolean getForce() { return force; } diff --git a/src/main/java/com/google/gcloud/datastore/EmbeddedEntityValue.java b/src/main/java/com/google/gcloud/datastore/EmbeddedEntityValue.java deleted file mode 100644 index d63313c6e9ff..000000000000 --- a/src/main/java/com/google/gcloud/datastore/EmbeddedEntityValue.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.google.gcloud.datastore; - -import static com.google.api.services.datastore.DatastoreV1.Value.ENTITY_VALUE_FIELD_NUMBER; - -import com.google.api.services.datastore.DatastoreV1; - -public final class EmbeddedEntityValue extends - Value { - - private static final long serialVersionUID = -5461475706792576395L; - - static final BaseMarshaller MARSHALLER = - new BaseMarshaller() { - - @Override - public int getProtoFieldId() { - return ENTITY_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(EmbeddedEntity value) { - return new Builder(value); - } - - @Override - protected EmbeddedEntity getValue(DatastoreV1.Value from) { - return EmbeddedEntity.fromPb(from.getEntityValue()); - } - - @Override - protected void setValue(EmbeddedEntityValue from, DatastoreV1.Value.Builder to) { - to.setEntityValue(from.get().toPb()); - } - }; - - public static final class Builder extends - Value.BaseBuilder { - - public Builder(EmbeddedEntity entity) { - super(Type.EMBEDDED_ENTITY); - indexed(false); - set(entity); - } - - @Override - public EmbeddedEntityValue build() { - return new EmbeddedEntityValue(this); - } - } - - public EmbeddedEntityValue(EmbeddedEntity entity) { - this(new Builder(entity)); - } - - EmbeddedEntityValue(Builder builder) { - super(builder); - } -} diff --git a/src/main/java/com/google/gcloud/datastore/Entity.java b/src/main/java/com/google/gcloud/datastore/Entity.java index 2d260b7d19b1..7673015bb227 100644 --- a/src/main/java/com/google/gcloud/datastore/Entity.java +++ b/src/main/java/com/google/gcloud/datastore/Entity.java @@ -6,102 +6,56 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSortedMap; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.io.ObjectStreamException; -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -public final class Entity implements Serializable { +public final class Entity extends PartialEntity { private static final long serialVersionUID = 432961565733066915L; - private final transient Key key; - private final transient ImmutableSortedMap> properties; - private transient DatastoreV1.Entity tempEntityPb; // only for deserialization - public static final class Builder { - private Key key; - private Map> properties; + private PartialEntity.Builder delegate; public Builder(Key key) { - this.key = checkNotNull(key); - this.properties = new HashMap<>(); + delegate = new PartialEntity.Builder(); + delegate.setKey(checkNotNull(key)); } public Builder(Entity entity) { - this.key = entity.key; - this.properties = new HashMap<>(entity.properties); + delegate = new PartialEntity.Builder(entity); } public Builder clearProperties() { - properties.clear(); + delegate.clearProperties(); return this; } public Builder removeProperty(String name) { - properties.remove(name); + delegate.removeProperty(name); return this; } public Builder setProperty(String name, Value value) { - properties.put(name, value); + delegate.setProperty(name, value); return this; } public Entity build() { - return new Entity(this); + PartialEntity entity = delegate.build(); + return new Entity((Key) entity.getKey(), entity.getProperties()); } } - private Entity(Builder builder) { - key = builder.key; - properties = ImmutableSortedMap.copyOf(builder.properties); - } - - public Key getKey() { - return key; - } - - public boolean hasProperty(String name) { - return properties.containsKey(name); - } - - public Value getProperty(String name) { - return properties.get(name); - } - - public Set getPropertyNames() { - return properties.keySet(); - } - - ImmutableSortedMap> getProperties() { - return properties; - } - - @Override - public String toString() { - return toPb().toString(); - } - - @Override - public int hashCode() { - return Objects.hash(key, properties); + private Entity(Key key, ImmutableSortedMap> properties) { + super(key, properties); } + /** + * Returns the entity's key (never null). + */ @Override - public boolean equals(Object obj) { - if (!(obj instanceof Entity)) { - return false; - } - Entity other = (Entity) obj; - return Objects.equals(key, other.key) - && Objects.equals(properties, other.properties); + public Key getKey() { + return (Key) super.getKey(); } static Entity fromPb(DatastoreV1.Entity entityPb) { @@ -113,31 +67,8 @@ static Entity fromPb(DatastoreV1.Entity entityPb) { return builder.build(); } - DatastoreV1.Entity toPb() { - DatastoreV1.Entity.Builder entityPb = DatastoreV1.Entity.newBuilder(); - entityPb.setKey(key.toPb()); - for (Map.Entry> entry : properties.entrySet()) { - DatastoreV1.Property.Builder propertyPb = DatastoreV1.Property.newBuilder(); - propertyPb.setName(entry.getKey()); - propertyPb.setValue(entry.getValue().toPb()); - entityPb.addProperty(propertyPb.build()); - } - return entityPb.build(); - } - - private void writeObject(ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeObject(toPb().toByteArray()); - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - byte[] bytes = (byte[]) in.readObject(); - tempEntityPb = DatastoreV1.Entity.parseFrom(bytes); - } - - @SuppressWarnings("unused") - private Object readResolve() throws ObjectStreamException { + @Override + protected Object readResolve() throws ObjectStreamException { return fromPb(tempEntityPb); } } diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index adad267b27b2..bc145a157b7b 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -16,43 +16,43 @@ /** * A key that is guaranteed to be complete. */ -public final class Key extends IncompleteKey { +public final class Key extends PartialKey { private static final long serialVersionUID = 3160994559785491356L; public static class Builder { - private final IncompleteKey.Builder keyBuilder; + private final PartialKey.Builder delegate; private String name; private Long id; public Builder(String dataset, String kind, String name) { - keyBuilder = new IncompleteKey.Builder(dataset, kind); + delegate = new PartialKey.Builder(dataset, kind); this.name = name; } public Builder(String dataset, String kind, long id) { - keyBuilder = new IncompleteKey.Builder(dataset, kind); + delegate = new PartialKey.Builder(dataset, kind); this.id = id; } - public Builder(IncompleteKey key, String name) { - keyBuilder = new IncompleteKey.Builder(key); + public Builder(PartialKey key, String name) { + delegate = new PartialKey.Builder(key); this.name = name; } - public Builder(IncompleteKey key, long id) { - keyBuilder = new IncompleteKey.Builder(key); + public Builder(PartialKey key, long id) { + delegate = new PartialKey.Builder(key); this.id = id; } public Builder setDataset(String dataset) { - keyBuilder.setDataset(checkNotNull(dataset)); + delegate.setDataset(checkNotNull(dataset)); return this; } public Builder setNamespace(String namespace) { - keyBuilder.setNamespace(checkNotNull(namespace)); + delegate.setNamespace(checkNotNull(namespace)); return this; } @@ -69,31 +69,31 @@ public Builder setId(long id) { } public Builder addToPath(String kind, long id) { - keyBuilder.addToPath(kind, id); + delegate.addToPath(kind, id); return this; } public Builder addToPath(String kind, String name) { - keyBuilder.addToPath(kind, name); + delegate.addToPath(kind, name); return this; } public Builder clearPath() { - keyBuilder.clearPath(); + delegate.clearPath(); return this; } public Key build() { - IncompleteKey key = keyBuilder.build(); + PartialKey key = delegate.build(); return id == null ? new Key(key, name) : new Key(key, id); } } - private Key(IncompleteKey key, String name) { + private Key(PartialKey key, String name) { super(key.getDataset(), key.getNamespace(), newPath(key, name)); } - private Key(IncompleteKey key, long id) { + private Key(PartialKey key, long id) { super(key.getDataset(), key.getNamespace(), newPath(key, id)); } @@ -136,7 +136,7 @@ public static Key fromUrlSafe(String urlSafe) { * @throws IllegalArgumentException if provided key is not complete. */ - public static Key fromIncompleteKey(IncompleteKey key) { + public static Key fromIncompleteKey(PartialKey key) { if (key instanceof Key) { return (Key) key; } @@ -150,23 +150,22 @@ public static Key fromIncompleteKey(IncompleteKey key) { } static Key fromPb(DatastoreV1.Key keyPb) { - return fromIncompleteKey(IncompleteKey.fromPb(keyPb)); + return fromIncompleteKey(PartialKey.fromPb(keyPb)); } @Override - @SuppressWarnings("unused") protected Object readResolve() throws ObjectStreamException { - return fromIncompleteKey((IncompleteKey) super.readResolve()); + return fromIncompleteKey((PartialKey) super.readResolve()); } - private static ImmutableList newPath(IncompleteKey key, String name) { + private static ImmutableList newPath(PartialKey key, String name) { ImmutableList.Builder path = ImmutableList.builder(); path.addAll(key.getAncestorPath()); path.add(new PathElement(key.getKind(), name)); return path.build(); } - private static ImmutableList newPath(IncompleteKey key, long id) { + private static ImmutableList newPath(PartialKey key, long id) { ImmutableList.Builder path = ImmutableList.builder(); path.addAll(key.getAncestorPath()); path.add(new PathElement(key.getKind(), id)); diff --git a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java new file mode 100644 index 000000000000..dcc5fecad742 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java @@ -0,0 +1,40 @@ +package com.google.gcloud.datastore; + +public final class KeyBuilder { + + private final PartialKey.Builder delegate; + + public KeyBuilder(String dataset, String namespace, String kind) { + delegate = new PartialKey.Builder(dataset, kind); + if (namespace != null) { + delegate.setNamespace(namespace); + } + } + + public KeyBuilder addToPath(String kind, String name) { + delegate.addToPath(kind, name); + return this; + } + + public KeyBuilder addToPath(String kind, long id) { + delegate.addToPath(kind, id); + return this; + } + + public KeyBuilder setNamespace(String namespace) { + delegate.setNamespace(namespace); + return this; + } + + public PartialKey build() { + return delegate.build(); + } + + public Key build(String name) { + return new Key.Builder(build(), name).build(); + } + + public Key build(long id) { + return new Key.Builder(build(), id).build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java similarity index 71% rename from src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java rename to src/main/java/com/google/gcloud/datastore/PartialEntity.java index 957c57ce9048..b96e4ad7b6e9 100644 --- a/src/main/java/com/google/gcloud/datastore/EmbeddedEntity.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java @@ -13,33 +13,29 @@ import java.util.Objects; import java.util.Set; -public final class EmbeddedEntity implements Serializable { +public class PartialEntity implements Serializable { private static final long serialVersionUID = 6492561268709192891L; - private final transient IncompleteKey key; + private final transient PartialKey key; private final transient ImmutableSortedMap> properties; - private transient DatastoreV1.Entity tempEntityPb; // only for deserialization + protected transient DatastoreV1.Entity tempEntityPb; // only for deserialization public static final class Builder { - private IncompleteKey key; - private Map> properties = new HashMap<>(); + private PartialKey key; + private Map> properties; public Builder() { + properties = new HashMap<>(); } - public Builder(EmbeddedEntity entity) { - this.key = entity.key; - this.properties = new HashMap<>(entity.properties); - } - - public Builder(Entity entity) { + public Builder(PartialEntity entity) { this.key = entity.getKey(); this.properties = new HashMap<>(entity.getProperties()); } - public Builder setKey(IncompleteKey key) { + public Builder setKey(PartialKey key) { this.key = key; return this; } @@ -59,25 +55,20 @@ public Builder setProperty(String name, Value value) { return this; } - public EmbeddedEntity build() { - return new EmbeddedEntity(this); + public PartialEntity build() { + return new PartialEntity(key, ImmutableSortedMap.copyOf(properties)); } } - private EmbeddedEntity(Builder builder) { - key = builder.key; - properties = ImmutableSortedMap.copyOf(builder.properties); - } - - public EmbeddedEntity(Entity entity) { - key = entity.getKey(); - properties = entity.getProperties(); + protected PartialEntity(PartialKey key, ImmutableSortedMap> properties) { + this.key = key; + this.properties = properties; } /** * Returns the key or null if not provided. */ - public IncompleteKey getKey() { + public PartialKey getKey() { return key; } @@ -105,18 +96,22 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (!(obj instanceof EmbeddedEntity)) { + if (!(obj instanceof PartialEntity)) { return false; } - EmbeddedEntity other = (EmbeddedEntity) obj; + PartialEntity other = (PartialEntity) obj; return Objects.equals(key, other.key) && Objects.equals(properties, other.properties); } - static EmbeddedEntity fromPb(DatastoreV1.Entity entityPb) { + ImmutableSortedMap> getProperties() { + return properties; + } + + static PartialEntity fromPb(DatastoreV1.Entity entityPb) { Builder builder = new Builder(); if (entityPb.hasKey()) { - builder.setKey(IncompleteKey.fromPb(entityPb.getKey())); + builder.setKey(PartialKey.fromPb(entityPb.getKey())); } for (DatastoreV1.Property property : entityPb.getPropertyList()) { builder.setProperty(property.getName(), Value.fromPb(property.getValue())); @@ -124,7 +119,7 @@ static EmbeddedEntity fromPb(DatastoreV1.Entity entityPb) { return builder.build(); } - DatastoreV1.Entity toPb() { + protected DatastoreV1.Entity toPb() { DatastoreV1.Entity.Builder entityPb = DatastoreV1.Entity.newBuilder(); if (key != null) { entityPb.setKey(key.toPb()); @@ -150,7 +145,7 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE } @SuppressWarnings("unused") - private Object readResolve() throws ObjectStreamException { + protected Object readResolve() throws ObjectStreamException { return fromPb(tempEntityPb); } } diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java b/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java new file mode 100644 index 000000000000..db076a3305a0 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java @@ -0,0 +1,58 @@ +package com.google.gcloud.datastore; + +import static com.google.api.services.datastore.DatastoreV1.Value.ENTITY_VALUE_FIELD_NUMBER; + +import com.google.api.services.datastore.DatastoreV1; + +public class PartialEntityValue extends + Value { + + private static final long serialVersionUID = -5461475706792576395L; + + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { + + @Override + public int getProtoFieldId() { + return ENTITY_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(PartialEntity value) { + return new Builder(value); + } + + @Override + protected PartialEntity getValue(DatastoreV1.Value from) { + return PartialEntity.fromPb(from.getEntityValue()); + } + + @Override + protected void setValue(PartialEntityValue from, DatastoreV1.Value.Builder to) { + to.setEntityValue(from.get().toPb()); + } + }; + + public static final class Builder extends + Value.BaseBuilder { + + public Builder(PartialEntity entity) { + super(Type.PARTIAL_ENTITY); + indexed(false); + set(entity); + } + + @Override + public PartialEntityValue build() { + return new PartialEntityValue(this); + } + } + + public PartialEntityValue(PartialEntity entity) { + this(new Builder(entity)); + } + + PartialEntityValue(Builder builder) { + super(builder); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/IncompleteKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java similarity index 94% rename from src/main/java/com/google/gcloud/datastore/IncompleteKey.java rename to src/main/java/com/google/gcloud/datastore/PartialKey.java index 8e67a39b5f0d..29856f7e11c0 100644 --- a/src/main/java/com/google/gcloud/datastore/IncompleteKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -19,7 +19,7 @@ import java.util.List; import java.util.Objects; -public class IncompleteKey implements Serializable { +public class PartialKey implements Serializable { private static final long serialVersionUID = -75301206578793347L; @@ -151,7 +151,7 @@ public Builder(String dataset, String kind) { this.kind = validateKind(kind); } - public Builder(IncompleteKey key) { + public Builder(PartialKey key) { dataset = key.dataset; namespace = key.namespace; kind = key.getKind(); @@ -201,15 +201,15 @@ public Builder setNamespace(String namespace) { return this; } - public IncompleteKey build() { + public PartialKey build() { PathElement leaf = new PathElement(kind); ImmutableList pathList = ImmutableList.builder().addAll(path).add(leaf).build(); - return new IncompleteKey(dataset, namespace, pathList); + return new PartialKey(dataset, namespace, pathList); } } - IncompleteKey(String dataset, String namespace, ImmutableList path) { + PartialKey(String dataset, String namespace, ImmutableList path) { checkState(!path.isEmpty(), "path must not be empty"); this.dataset = dataset; this.namespace = namespace; @@ -244,10 +244,10 @@ public int hashCode() { @Override public boolean equals(Object other) { - if (!(other instanceof IncompleteKey)) { + if (!(other instanceof PartialKey)) { return false; } - IncompleteKey otherKey = (IncompleteKey) other; + PartialKey otherKey = (PartialKey) other; return Objects.equals(dataset, otherKey.dataset) && Objects.equals(namespace, otherKey.namespace) && Objects.equals(path, otherKey.path); @@ -257,7 +257,7 @@ PathElement getLeaf() { return path.get(path.size() - 1); } - static IncompleteKey fromPb(DatastoreV1.Key keyPb) { + static PartialKey fromPb(DatastoreV1.Key keyPb) { String dataset = null; String namespace = null; if (keyPb.hasPartitionId()) { @@ -273,7 +273,7 @@ static IncompleteKey fromPb(DatastoreV1.Key keyPb) { for (DatastoreV1.Key.PathElement pathElementPb : keyPb.getPathElementList()) { pathBuilder.add(PathElement.fromPb(pathElementPb)); } - return new IncompleteKey(dataset, namespace, pathBuilder.build()); + return new PartialKey(dataset, namespace, pathBuilder.build()); } DatastoreV1.Key toPb() { diff --git a/src/main/java/com/google/gcloud/datastore/Transaction.java b/src/main/java/com/google/gcloud/datastore/Transaction.java new file mode 100644 index 000000000000..ba9289aa8e4e --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/Transaction.java @@ -0,0 +1,8 @@ +package com.google.gcloud.datastore; + +public interface Transaction extends DatastoreReader, DatastoreWriter { + + void commit(); + + void rollback(); +} \ No newline at end of file diff --git a/src/main/java/com/google/gcloud/datastore/TransactionOption.java b/src/main/java/com/google/gcloud/datastore/TransactionOption.java new file mode 100644 index 000000000000..50bd2aac2869 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/TransactionOption.java @@ -0,0 +1,59 @@ +package com.google.gcloud.datastore; + +import java.io.Serializable; + +public abstract class TransactionOption implements Serializable { + + private static final long serialVersionUID = -1862234444015690375L; + + + TransactionOption() { + // package protected + } + + public static final class IsolationLevel extends TransactionOption { + + private static final long serialVersionUID = -5592165378565409515L; + + private final Level level; + + public enum Level { + SERIALIZABLE, SNAPSHOT; + } + + public IsolationLevel(Level level) { + this.level = level; + } + + public Level getLevel() { + return level; + } + } + + public static final class ForceWrites extends TransactionOption { + + private static final long serialVersionUID = 7448106703678852594L; + + private final boolean force; + + public ForceWrites(boolean force) { + this.force = force; + } + + public boolean isForce() { + return force; + } + } + + public static IsolationLevel serializable() { + return new IsolationLevel(IsolationLevel.Level.SERIALIZABLE); + } + + public static IsolationLevel snapshot() { + return new IsolationLevel(IsolationLevel.Level.SNAPSHOT); + } + + public static ForceWrites forceWrites() { + return new ForceWrites(true); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/Value.java b/src/main/java/com/google/gcloud/datastore/Value.java index 2a685eaf5acd..4978b14bd6ec 100644 --- a/src/main/java/com/google/gcloud/datastore/Value.java +++ b/src/main/java/com/google/gcloud/datastore/Value.java @@ -30,7 +30,7 @@ public enum Type { NULL(NullValue.MARSHALLER, NullValue.MARSHALLER), STRING(StringValue.MARSHALLER, StringValue.MARSHALLER), - EMBEDDED_ENTITY(EmbeddedEntityValue.MARSHALLER, EmbeddedEntityValue.MARSHALLER), + PARTIAL_ENTITY(PartialEntityValue.MARSHALLER, PartialEntityValue.MARSHALLER), LIST(ListValue.MARSHALLER, ListValue.MARSHALLER), KEY(KeyValue.MARSHALLER, KeyValue.MARSHALLER); diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java new file mode 100644 index 000000000000..7f4f497dd970 --- /dev/null +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -0,0 +1,69 @@ +package com.google.gcloud.datastore; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class DatastoreServiceTest { + + @Test + public void testGetOptions() { + fail("Not yet implemented"); + } + + @Test + public void testNewTransaction() { + fail("Not yet implemented"); + } + + @Test + public void testNewBatchWriter() { + fail("Not yet implemented"); + } + + @Test + public void testAllocateId() { + fail("Not yet implemented"); + } + + @Test + public void testAllocateIds() { + fail("Not yet implemented"); + } + + @Test + public void testGetKey() { + fail("Not yet implemented"); + } + + @Test + public void testGetKeyArray() { + fail("Not yet implemented"); + } + + @Test + public void testAdd() { + fail("Not yet implemented"); + } + + @Test + public void testUpdate() { + fail("Not yet implemented"); + } + + @Test + public void testPut() { + fail("Not yet implemented"); + } + + @Test + public void testDelete() { + fail("Not yet implemented"); + } + + @Test + public void testNewKeyBuilder() { + fail("Not yet implemented"); + } + +} diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 48a91eceb8e4..838c0a2079e7 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -15,8 +15,8 @@ public class SerializationTest { - private static final IncompleteKey INCOMPLETE_KEY = - new IncompleteKey.Builder("ds", "k").addToPath("p", 1).build(); + private static final PartialKey INCOMPLETE_KEY = + new PartialKey.Builder("ds", "k").addToPath("p", 1).build(); private static final Key KEY1 = new Key.Builder("ds", "k", "n").build(); private static final Key KEY2 = new Key.Builder(INCOMPLETE_KEY, 2).build(); private static final KeyValue KEY_PROPERTY = new KeyValue(KEY1); @@ -26,20 +26,20 @@ public class SerializationTest { private static final Entity ENTITY1 = new Entity.Builder(KEY1).build(); private static final Entity ENTITY2 = new Entity.Builder(KEY2).setProperty("null", new NullValue()).build(); - private static final EmbeddedEntity EMBEDDED_ENTITY1 = new EmbeddedEntity(ENTITY1); - private static final EmbeddedEntity EMBEDDED_ENTITY2 = new EmbeddedEntity(ENTITY2); - private static final EmbeddedEntity EMBEDDED_ENTITY3 = - new EmbeddedEntity.Builder() + private static final PartialEntity EMBEDDED_ENTITY1 = ENTITY1; + private static final PartialEntity EMBEDDED_ENTITY2 = ENTITY2; + private static final PartialEntity EMBEDDED_ENTITY3 = + new PartialEntity.Builder() .setKey(INCOMPLETE_KEY) .setProperty("p1", STRING_PROPERTY) .setProperty("p2", STRING_PROPERTY) .build(); - private static final EmbeddedEntityValue EMBEDDED_ENTITY_PROPERTY1 = - new EmbeddedEntityValue(EMBEDDED_ENTITY1); - private static final EmbeddedEntityValue EMBEDDED_ENTITY_PROPERTY2 = - new EmbeddedEntityValue(EMBEDDED_ENTITY2); - private static final EmbeddedEntityValue EMBEDDED_ENTITY_PROPERTY3 = - new EmbeddedEntityValue(EMBEDDED_ENTITY3); + private static final PartialEntityValue EMBEDDED_ENTITY_PROPERTY1 = + new PartialEntityValue(EMBEDDED_ENTITY1); + private static final PartialEntityValue EMBEDDED_ENTITY_PROPERTY2 = + new PartialEntityValue(EMBEDDED_ENTITY2); + private static final PartialEntityValue EMBEDDED_ENTITY_PROPERTY3 = + new PartialEntityValue(EMBEDDED_ENTITY3); private static final ListValue PROPERTY_LIST_PROPERTY = new ListValue.Builder() .addValue(NULL_PROPERTY) @@ -51,7 +51,7 @@ public class SerializationTest { Type.NULL, array(NULL_PROPERTY), Type.KEY, array(KEY_PROPERTY), Type.STRING, array(STRING_PROPERTY), - Type.EMBEDDED_ENTITY, array(EMBEDDED_ENTITY_PROPERTY1, EMBEDDED_ENTITY_PROPERTY2, + Type.PARTIAL_ENTITY, array(EMBEDDED_ENTITY_PROPERTY1, EMBEDDED_ENTITY_PROPERTY2, EMBEDDED_ENTITY_PROPERTY3), Type.LIST, array(PROPERTY_LIST_PROPERTY)); From c8c83045df5330a98888e6a3b73f126f9449a859 Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 2 Dec 2014 16:33:23 -0800 Subject: [PATCH 024/771] work in progress (query) --- .../com/google/gcloud/datastore/Cursor.java | 68 ++++++++++ .../gcloud/datastore/DatastoreReader.java | 8 +- .../gcloud/datastore/DatastoreService.java | 60 ++++++--- .../datastore/DatastoreServiceImpl.java | 14 ++- .../gcloud/datastore/DatastoreWriter.java | 23 +++- .../com/google/gcloud/datastore/Entity.java | 13 +- .../com/google/gcloud/datastore/GqlQuery.java | 26 ++++ .../google/gcloud/datastore/KeyBuilder.java | 34 +++-- .../gcloud/datastore/PartialEntity.java | 54 +++----- .../google/gcloud/datastore/PartialKey.java | 116 ++++++------------ .../com/google/gcloud/datastore/Query.java | 8 ++ .../google/gcloud/datastore/QueryResult.java | 36 ++++++ .../google/gcloud/datastore/Serializable.java | 48 ++++++++ .../google/gcloud/datastore/Transaction.java | 5 +- .../com/google/gcloud/datastore/Value.java | 29 ++--- .../gcloud/datastore/SerializationTest.java | 6 +- 16 files changed, 364 insertions(+), 184 deletions(-) create mode 100644 src/main/java/com/google/gcloud/datastore/Cursor.java create mode 100644 src/main/java/com/google/gcloud/datastore/GqlQuery.java create mode 100644 src/main/java/com/google/gcloud/datastore/Query.java create mode 100644 src/main/java/com/google/gcloud/datastore/QueryResult.java create mode 100644 src/main/java/com/google/gcloud/datastore/Serializable.java diff --git a/src/main/java/com/google/gcloud/datastore/Cursor.java b/src/main/java/com/google/gcloud/datastore/Cursor.java new file mode 100644 index 000000000000..51f2652b64cc --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/Cursor.java @@ -0,0 +1,68 @@ +package com.google.gcloud.datastore; + +import static com.google.api.client.repackaged.com.google.common.base.Preconditions.checkNotNull; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.protobuf.ByteString; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Arrays; + +public final class Cursor implements Serializable { + + private static final long serialVersionUID = -1423744878777486541L; + + private byte[] bytes; + + public Cursor(byte[] bytes) { + this.bytes = checkNotNull(bytes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(bytes); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Cursor)) { + return false; + } + + return Arrays.equals(bytes, ((Cursor) obj).bytes); + } + + @Override + public String toString() { + return toPb().toString(); + } + + public String toUrlSafe() { + try { + return URLEncoder.encode(toString(), UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unxpeced encoding exception", e); + } + } + + ByteString toPb() { + return ByteString.copyFrom(bytes); + } + + public static Cursor fromUrlSafe(String urlSafe) { + try { + String utf8Str = URLDecoder.decode(urlSafe, UTF_8.name()); + ByteString byteString = ByteString.copyFromUtf8(utf8Str); + return fromPb(byteString); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unxpeced decoding exception", e); + } + } + + private static Cursor fromPb(ByteString byteString) { + return new Cursor(byteString.toByteArray()); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java index a1eb56aab46a..1843d147165e 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java @@ -2,6 +2,9 @@ import java.util.Iterator; +/** + * An interface to represent Google Cloud Datastore read operations. + */ public interface DatastoreReader { Entity get(Key key); @@ -9,6 +12,5 @@ public interface DatastoreReader { // results are returned using request order Iterator get(Key... key); - // query result item is a tuple of (key, value...) where values may be empty - //QueryResult runQuery(Query query); -} \ No newline at end of file + QueryResult runQuery(Query query); +} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index 88993173bbb8..d118b9b6c71e 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -2,27 +2,20 @@ import java.util.Iterator; +/** + * An interface for Google Cloud Datastore. + */ public interface DatastoreService extends DatastoreReader, DatastoreWriter { - enum QueryType { - PROJECTION, - FULL; - } - - /* - interface QueryResult { - - } - - // consider 2 types of queries regualr and Gql - interface Query { - QueryResult keysOnly(QueryType query); - } -*/ - - + /** + * Returns the {@code DatastoreServiceOptions} for this service. + */ DatastoreServiceOptions getOptions(); + /** + * Returns a key builder for the requested {@code kind}. + * The key would be initialized with the this service dataset and default namespace. + */ KeyBuilder newKeyBuilder(String kind); /** @@ -32,6 +25,10 @@ interface Query { */ Transaction newTransaction(TransactionOption... transactionOption); + /** + * Returns a new Batch writer for processing multiple write operations + * in one request. + */ BatchWriter newBatchWriter(BatchWriteOption... batchWriteOption); /** @@ -46,6 +43,33 @@ interface Query { * * @throws DatastoreServiceExcepiton upon failure */ - // results are returned using request order Iterator allocateIds(PartialKey... key); + + /** + * @see DatastoreWriter#add(Entity...) + * @throws DatastoreServiceExcepiton upon failure + */ + @Override + void add(Entity... entity); + + /** + * @see DatastoreWriter#update(Entity...) + * @throws DatastoreServiceExcepiton upon failure + */ + @Override + void update(Entity... entity); + + /** + * @see DatastoreWriter#put(Entity...) + * @throws DatastoreServiceExcepiton upon failure + */ + @Override + void put(Entity... entity); + + /** + * @see DatastoreWriter#delete(Key...) + * @throws DatastoreServiceExcepiton upon failure + */ + @Override + void delete(Key... key); } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index cd5475c12bab..dd6f957b92f9 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -55,29 +55,31 @@ public Iterator get(Key... key) { @Override public void add(Entity... entity) { // TODO Auto-generated method stub - } @Override public void update(Entity... entity) { // TODO Auto-generated method stub - } @Override - public Key put(Entity... entity) { + public void put(Entity... entity) { // TODO Auto-generated method stub - return null; } @Override public void delete(Key... key) { // TODO Auto-generated method stub - } @Override public KeyBuilder newKeyBuilder(String kind) { - return new KeyBuilder(kind, options.getDefaultNamespace(), kind); + return new KeyBuilder(this, kind); + } + + @Override + public QueryResult runQuery(Query query) { + // TODO Auto-generated method stub + return null; } } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java b/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java index 005df9ff6800..cfb864744099 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java @@ -1,12 +1,31 @@ package com.google.gcloud.datastore; +/** + * An interface to represent Google Cloud Datastore write operations. + */ public interface DatastoreWriter { + /** + * A Datastore add operation. + * The operation will fail if an entity already exists. + */ void add(Entity... entity); + /** + * A Datastore update operation. + * The operation will fail if an entity does not already exist. + */ void update(Entity... entity); - Key put(Entity... entity); + /** + * A Datastore put (a.k.a upsert) operation. + * The operation will add or modify the entities. + */ + void put(Entity... entity); + /** + * A datastore delete operation. + * It is OK request a deletion of a non-existing entity. + */ void delete(Key... key); -} \ No newline at end of file +} diff --git a/src/main/java/com/google/gcloud/datastore/Entity.java b/src/main/java/com/google/gcloud/datastore/Entity.java index 7673015bb227..b1241a16ed16 100644 --- a/src/main/java/com/google/gcloud/datastore/Entity.java +++ b/src/main/java/com/google/gcloud/datastore/Entity.java @@ -5,8 +5,7 @@ import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSortedMap; - -import java.io.ObjectStreamException; +import com.google.protobuf.InvalidProtocolBufferException; public final class Entity extends PartialEntity { @@ -58,6 +57,11 @@ public Key getKey() { return (Key) super.getKey(); } + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + return fromPb(DatastoreV1.Entity.parseFrom(bytesPb)); + } + static Entity fromPb(DatastoreV1.Entity entityPb) { Preconditions.checkArgument(entityPb.hasKey()); Builder builder = new Builder(Key.fromPb(entityPb.getKey())); @@ -66,9 +70,4 @@ static Entity fromPb(DatastoreV1.Entity entityPb) { } return builder.build(); } - - @Override - protected Object readResolve() throws ObjectStreamException { - return fromPb(tempEntityPb); - } } diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java new file mode 100644 index 000000000000..b99ca22ad226 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java @@ -0,0 +1,26 @@ +package com.google.gcloud.datastore; + +import com.google.api.services.datastore.DatastoreV1; +import com.google.protobuf.InvalidProtocolBufferException; + +public class GqlQuery extends Query { + + private static final long serialVersionUID = 5988280590929540569L; + + @Override + protected DatastoreV1.GqlQuery toPb() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + // TODO Auto-generated method stub + return null; + } + + static GqlQuery fromPb(DatastoreV1.GqlQuery queryPb) { + // TODO Auto-generated method stub + return null; + } +} diff --git a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java index dcc5fecad742..78f6fc1790de 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java +++ b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java @@ -1,14 +1,22 @@ package com.google.gcloud.datastore; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * An helper for creating keys for a {@link DatastoreService}. + */ public final class KeyBuilder { private final PartialKey.Builder delegate; - - public KeyBuilder(String dataset, String namespace, String kind) { - delegate = new PartialKey.Builder(dataset, kind); - if (namespace != null) { - delegate.setNamespace(namespace); - } + private final DatastoreService service; + + /** + * Constructing a KeyBuilder. + */ + public KeyBuilder(DatastoreService service, String kind) { + this.service = checkNotNull(service); + delegate = new PartialKey.Builder(service.getOptions().getDataset(), kind); + delegate.setNamespace(service.getOptions().getDefaultNamespace()); } public KeyBuilder addToPath(String kind, String name) { @@ -26,8 +34,12 @@ public KeyBuilder setNamespace(String namespace) { return this; } - public PartialKey build() { - return delegate.build(); + /** + * Builds a key with a newly allocated id. + * @throws DatastoreServiceException if allocation failed. + */ + public Key allocateIdAndBuild() { + return service.allocateId(build()); } public Key build(String name) { @@ -37,4 +49,8 @@ public Key build(String name) { public Key build(long id) { return new Key.Builder(build(), id).build(); } -} \ No newline at end of file + + public PartialKey build() { + return delegate.build(); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java index b96e4ad7b6e9..d97e168c27c6 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java @@ -2,24 +2,19 @@ import com.google.api.services.datastore.DatastoreV1; import com.google.common.collect.ImmutableSortedMap; +import com.google.protobuf.InvalidProtocolBufferException; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamException; -import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; -public class PartialEntity implements Serializable { +public class PartialEntity extends Serializable { private static final long serialVersionUID = 6492561268709192891L; private final transient PartialKey key; private final transient ImmutableSortedMap> properties; - protected transient DatastoreV1.Entity tempEntityPb; // only for deserialization public static final class Builder { @@ -84,9 +79,8 @@ public Set getPropertyNames() { return properties.keySet(); } - @Override - public String toString() { - return toPb().toString(); + ImmutableSortedMap> getProperties() { + return properties; } @Override @@ -104,21 +98,7 @@ public boolean equals(Object obj) { && Objects.equals(properties, other.properties); } - ImmutableSortedMap> getProperties() { - return properties; - } - - static PartialEntity fromPb(DatastoreV1.Entity entityPb) { - Builder builder = new Builder(); - if (entityPb.hasKey()) { - builder.setKey(PartialKey.fromPb(entityPb.getKey())); - } - for (DatastoreV1.Property property : entityPb.getPropertyList()) { - builder.setProperty(property.getName(), Value.fromPb(property.getValue())); - } - return builder.build(); - } - + @Override protected DatastoreV1.Entity toPb() { DatastoreV1.Entity.Builder entityPb = DatastoreV1.Entity.newBuilder(); if (key != null) { @@ -133,19 +113,19 @@ protected DatastoreV1.Entity toPb() { return entityPb.build(); } - private void writeObject(ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeObject(toPb().toByteArray()); - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - byte[] bytes = (byte[]) in.readObject(); - tempEntityPb = DatastoreV1.Entity.parseFrom(bytes); + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + return fromPb(DatastoreV1.Entity.parseFrom(bytesPb)); } - @SuppressWarnings("unused") - protected Object readResolve() throws ObjectStreamException { - return fromPb(tempEntityPb); + static PartialEntity fromPb(DatastoreV1.Entity entityPb) { + Builder builder = new Builder(); + if (entityPb.hasKey()) { + builder.setKey(PartialKey.fromPb(entityPb.getKey())); + } + for (DatastoreV1.Property property : entityPb.getPropertyList()) { + builder.setProperty(property.getName(), Value.fromPb(property.getValue())); + } + return builder.build(); } } diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index 29856f7e11c0..261e8b2b3f6b 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -9,33 +9,27 @@ import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; +import com.google.protobuf.InvalidProtocolBufferException; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamException; -import java.io.Serializable; import java.util.LinkedList; import java.util.List; import java.util.Objects; -public class PartialKey implements Serializable { +public class PartialKey extends Serializable { private static final long serialVersionUID = -75301206578793347L; private final transient String dataset; private final transient String namespace; private final transient ImmutableList path; - private transient DatastoreV1.Key tempKeyPb; // only for deserialization - public static final class PathElement implements Serializable { + public static final class PathElement extends Serializable { private static final long serialVersionUID = -7968078857690784595L; private final transient String kind; private final transient Long id; private final transient String name; - private transient DatastoreV1.Key.PathElement tempPathElementPb; // only for deserialization private PathElement(String kind) { this(kind, null); @@ -77,11 +71,6 @@ public Object getNameOrId() { return id == null ? name : id; } - @Override - public String toString() { - return toPb().toString(); - } - @Override public int hashCode() { return Objects.hash(kind, id, name); @@ -98,17 +87,8 @@ public boolean equals(Object obj) { && Objects.equals(name, other.name); } - static PathElement fromPb(DatastoreV1.Key.PathElement pathElementPb) { - String kind = pathElementPb.getKind(); - if (pathElementPb.hasId()) { - return new PathElement(kind, pathElementPb.getId()); - } else if (pathElementPb.hasName()) { - return new PathElement(kind, pathElementPb.getName()); - } - return new PathElement(kind); - } - - DatastoreV1.Key.PathElement toPb() { + @Override + protected DatastoreV1.Key.PathElement toPb() { DatastoreV1.Key.PathElement.Builder pathElementPb = DatastoreV1.Key.PathElement.newBuilder(); pathElementPb.setKind(kind); if (id != null) { @@ -119,20 +99,19 @@ DatastoreV1.Key.PathElement toPb() { return pathElementPb.build(); } - private void writeObject(ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeObject(toPb().toByteArray()); - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - byte[] bytes = (byte[]) in.readObject(); - tempPathElementPb = DatastoreV1.Key.PathElement.parseFrom(bytes); + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + return fromPb(DatastoreV1.Key.PathElement.parseFrom(bytesPb)); } - @SuppressWarnings("unused") - private Object readResolve() throws ObjectStreamException { - return fromPb(tempPathElementPb); + static PathElement fromPb(DatastoreV1.Key.PathElement pathElementPb) { + String kind = pathElementPb.getKind(); + if (pathElementPb.hasId()) { + return new PathElement(kind, pathElementPb.getId()); + } else if (pathElementPb.hasName()) { + return new PathElement(kind, pathElementPb.getName()); + } + return new PathElement(kind); } } @@ -232,11 +211,6 @@ public String getKind() { return getLeaf().getKind(); } - @Override - public String toString() { - return toPb().toString(); - } - @Override public int hashCode() { return Objects.hash(dataset, namespace, path); @@ -253,30 +227,8 @@ public boolean equals(Object other) { && Objects.equals(path, otherKey.path); } - PathElement getLeaf() { - return path.get(path.size() - 1); - } - - static PartialKey fromPb(DatastoreV1.Key keyPb) { - String dataset = null; - String namespace = null; - if (keyPb.hasPartitionId()) { - DatastoreV1.PartitionId partitionIdPb = keyPb.getPartitionId(); - if (partitionIdPb.hasDatasetId()) { - dataset = partitionIdPb.getDatasetId(); - } - if (partitionIdPb.hasNamespace()) { - namespace = partitionIdPb.getNamespace(); - } - } - ImmutableList.Builder pathBuilder = ImmutableList.builder(); - for (DatastoreV1.Key.PathElement pathElementPb : keyPb.getPathElementList()) { - pathBuilder.add(PathElement.fromPb(pathElementPb)); - } - return new PartialKey(dataset, namespace, pathBuilder.build()); - } - - DatastoreV1.Key toPb() { + @Override + protected DatastoreV1.Key toPb() { DatastoreV1.Key.Builder keyPb = DatastoreV1.Key.newBuilder(); DatastoreV1.PartitionId.Builder partitionIdPb = DatastoreV1.PartitionId.newBuilder(); if (dataset != null) { @@ -294,19 +246,31 @@ DatastoreV1.Key toPb() { return keyPb.build(); } - private void writeObject(ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeObject(toPb().toByteArray()); + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + return fromPb(DatastoreV1.Key.parseFrom(bytesPb)); } - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - byte[] bytes = (byte[]) in.readObject(); - tempKeyPb = DatastoreV1.Key.parseFrom(bytes); + static PartialKey fromPb(DatastoreV1.Key keyPb) { + String dataset = null; + String namespace = null; + if (keyPb.hasPartitionId()) { + DatastoreV1.PartitionId partitionIdPb = keyPb.getPartitionId(); + if (partitionIdPb.hasDatasetId()) { + dataset = partitionIdPb.getDatasetId(); + } + if (partitionIdPb.hasNamespace()) { + namespace = partitionIdPb.getNamespace(); + } + } + ImmutableList.Builder pathBuilder = ImmutableList.builder(); + for (DatastoreV1.Key.PathElement pathElementPb : keyPb.getPathElementList()) { + pathBuilder.add(PathElement.fromPb(pathElementPb)); + } + return new PartialKey(dataset, namespace, pathBuilder.build()); } - @SuppressWarnings("unused") - protected Object readResolve() throws ObjectStreamException { - return fromPb(tempKeyPb); + PathElement getLeaf() { + return path.get(path.size() - 1); } } diff --git a/src/main/java/com/google/gcloud/datastore/Query.java b/src/main/java/com/google/gcloud/datastore/Query.java new file mode 100644 index 000000000000..cff0b13aaf73 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/Query.java @@ -0,0 +1,8 @@ +package com.google.gcloud.datastore; + +import com.google.protobuf.GeneratedMessage; + +public abstract class Query extends Serializable { + + private static final long serialVersionUID = 4960655852209261775L; +} diff --git a/src/main/java/com/google/gcloud/datastore/QueryResult.java b/src/main/java/com/google/gcloud/datastore/QueryResult.java new file mode 100644 index 000000000000..d47edc484ddd --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/QueryResult.java @@ -0,0 +1,36 @@ +package com.google.gcloud.datastore; + +import java.util.Iterator; + +/** + * A result of Google Cloud Datastore query. + */ +public interface QueryResult extends Iterator { + + /** + * The type of the result. + * Full: A complete {@link Entity}. + * PROJECTION: A partial entity, represented by {@link PartialEntity}. + * KEY_ONLY: An entity's {@link Key}. + */ + enum ResultType { + FULL, + PROJECTION, + KEY_ONLY; + } + + /** + * This method should be used to cast the result to its appropriate type. + *

 {@code
+   * ResultType.FULL -> (QueryResult)
+   * ResultType.PROJECTION -> (QueryResult)
+   * ResultType.KEY_ONLY -> (QueryResult)
+   * } 
+ */ + ResultType getResultType(); + + /** + * Return the Cursor for the next result. Not currently implemented (depends on v1beta3). + */ + Cursor getCursor(); +} diff --git a/src/main/java/com/google/gcloud/datastore/Serializable.java b/src/main/java/com/google/gcloud/datastore/Serializable.java new file mode 100644 index 000000000000..c9f6b7984512 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/Serializable.java @@ -0,0 +1,48 @@ +package com.google.gcloud.datastore; + +import com.google.protobuf.GeneratedMessage; +import com.google.protobuf.InvalidProtocolBufferException; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.StreamCorruptedException; + +abstract class Serializable implements java.io.Serializable { + + private static final long serialVersionUID = -5565522710061949199L; + + private transient byte[] bytesPb; // only for deserialization + + @Override + public String toString() { + return toPb().toString(); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(toPb().toByteArray()); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + bytesPb = (byte[]) in.readObject(); + } + + protected Object readResolve() throws ObjectStreamException { + try { + return fromPb(bytesPb); + } catch (InvalidProtocolBufferException ex) { + StreamCorruptedException sce = new StreamCorruptedException("Failed to create object"); + sce.initCause(ex); + throw sce; + } finally { + bytesPb = null; + } + } + + protected abstract M toPb(); + + protected abstract Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException; +} diff --git a/src/main/java/com/google/gcloud/datastore/Transaction.java b/src/main/java/com/google/gcloud/datastore/Transaction.java index ba9289aa8e4e..4df12134c3ed 100644 --- a/src/main/java/com/google/gcloud/datastore/Transaction.java +++ b/src/main/java/com/google/gcloud/datastore/Transaction.java @@ -1,8 +1,11 @@ package com.google.gcloud.datastore; +/** + * A Google cloud datastore transaction. + */ public interface Transaction extends DatastoreReader, DatastoreWriter { void commit(); void rollback(); -} \ No newline at end of file +} diff --git a/src/main/java/com/google/gcloud/datastore/Value.java b/src/main/java/com/google/gcloud/datastore/Value.java index 4978b14bd6ec..edc197ddd696 100644 --- a/src/main/java/com/google/gcloud/datastore/Value.java +++ b/src/main/java/com/google/gcloud/datastore/Value.java @@ -5,18 +5,14 @@ import com.google.api.services.datastore.DatastoreV1; import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.InvalidProtocolBufferException; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamException; -import java.io.Serializable; import java.util.Objects; // TODO: add javadoc, and mention that null should only be represented by NullValue. public abstract class Value, B extends Value.Builder> - implements Serializable { + extends Serializable { private static final long serialVersionUID = -1899638277588872742L; @@ -24,7 +20,6 @@ private final transient boolean indexed; private final transient Integer meaning; private final transient V value; - private transient DatastoreV1.Value tempValuePb; // only for deserialization public enum Type { @@ -262,8 +257,9 @@ public boolean equals(Object other) { && Objects.equals(value, otherValue.get()); } + @Override @SuppressWarnings("unchecked") - DatastoreV1.Value toPb() { + protected DatastoreV1.Value toPb() { Marshaller marshaller = getType().getMarshaller(); return marshaller.toProto((P) this); } @@ -293,19 +289,8 @@ DatastoreV1.Value toPb() { return marshaller.fromProto(proto).build(); } - private void writeObject(ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeObject(toPb().toByteArray()); - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - byte[] bytes = (byte[]) in.readObject(); - tempValuePb = DatastoreV1.Value.parseFrom(bytes); - } - - @SuppressWarnings("unused") - protected Object readResolve() throws ObjectStreamException { - return fromPb(tempValuePb); + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + return fromPb(DatastoreV1.Value.parseFrom(bytesPb)); } } diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 838c0a2079e7..0184f26717b9 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -62,9 +62,9 @@ private static T[] array(T... values) { @Test public void testValues() throws Exception { for (Type type : Type.values()) { - Value[] values = typeToValues.get(type); - for (Value value : values) { - Value copy = serialiazeAndDeserialize(value); + Value[] values = typeToValues.get(type); + for (Value value : values) { + Value copy = serialiazeAndDeserialize(value); assertEquals(value, copy); assertEquals(value.get(), copy.get()); } From ab71adf45cffda1dd40c193dd50b2bf59473c266 Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 3 Dec 2014 18:41:55 -0800 Subject: [PATCH 025/771] added all types --- pom.xml | 5 + .../google/gcloud/datastore/BatchWriter.java | 7 +- .../com/google/gcloud/datastore/Blob.java | 128 ++++++++++++ .../google/gcloud/datastore/BlobValue.java | 55 +++++ .../google/gcloud/datastore/BooleanValue.java | 55 +++++ .../com/google/gcloud/datastore/Cursor.java | 12 +- .../gcloud/datastore/DatastoreReader.java | 17 +- .../gcloud/datastore/DatastoreService.java | 2 +- .../datastore/DatastoreServiceImpl.java | 7 +- .../google/gcloud/datastore/DateAndTime.java | 74 +++++++ .../gcloud/datastore/DateAndTimeValue.java | 57 +++++ .../google/gcloud/datastore/DoubleValue.java | 55 +++++ .../com/google/gcloud/datastore/Entity.java | 43 ++-- .../com/google/gcloud/datastore/GqlQuery.java | 2 +- .../java/com/google/gcloud/datastore/Key.java | 140 +++++++++---- .../google/gcloud/datastore/KeyBuilder.java | 8 +- .../com/google/gcloud/datastore/KeyValue.java | 4 +- .../google/gcloud/datastore/ListValue.java | 7 +- .../google/gcloud/datastore/LongValue.java | 55 +++++ .../gcloud/datastore/PartialEntity.java | 55 +++-- .../gcloud/datastore/PartialEntityValue.java | 4 +- .../google/gcloud/datastore/PartialKey.java | 73 ++++--- .../com/google/gcloud/datastore/Query.java | 5 +- .../google/gcloud/datastore/QueryResult.java | 39 ++-- .../com/google/gcloud/datastore/RawValue.java | 59 ++++++ .../google/gcloud/datastore/Serializable.java | 17 ++ .../google/gcloud/datastore/StringValue.java | 6 +- .../google/gcloud/datastore/Transaction.java | 10 + .../com/google/gcloud/datastore/Value.java | 196 ++++++++++++------ .../datastore/DatastoreServiceTest.java | 3 +- .../gcloud/datastore/SerializationTest.java | 106 ++++++---- 31 files changed, 1051 insertions(+), 255 deletions(-) create mode 100644 src/main/java/com/google/gcloud/datastore/Blob.java create mode 100644 src/main/java/com/google/gcloud/datastore/BlobValue.java create mode 100644 src/main/java/com/google/gcloud/datastore/BooleanValue.java create mode 100644 src/main/java/com/google/gcloud/datastore/DateAndTime.java create mode 100644 src/main/java/com/google/gcloud/datastore/DateAndTimeValue.java create mode 100644 src/main/java/com/google/gcloud/datastore/DoubleValue.java create mode 100644 src/main/java/com/google/gcloud/datastore/LongValue.java create mode 100644 src/main/java/com/google/gcloud/datastore/RawValue.java diff --git a/pom.xml b/pom.xml index d82863493cd8..478c89fd38d0 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,11 @@ test RELEASE + + joda-time + joda-time + RELEASE + diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriter.java b/src/main/java/com/google/gcloud/datastore/BatchWriter.java index 9d2e74f01cb1..c8e1056b21d9 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchWriter.java +++ b/src/main/java/com/google/gcloud/datastore/BatchWriter.java @@ -2,5 +2,10 @@ public interface BatchWriter extends DatastoreWriter { + /** + * Submit the batch to the Datastore. + * + * @throws DatastoreServiceException if there was any failure. + */ void submit(); -} \ No newline at end of file +} diff --git a/src/main/java/com/google/gcloud/datastore/Blob.java b/src/main/java/com/google/gcloud/datastore/Blob.java new file mode 100644 index 000000000000..e7e134815236 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/Blob.java @@ -0,0 +1,128 @@ +package com.google.gcloud.datastore; + +import static com.google.api.client.util.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.protobuf.ByteString; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Objects; + +/** + * A Google Cloud Datastore Blob. + * A Datastore blob is limited to {@value #MAX_LENGTH} bytes. + * This class is immutable. + * + * @see Google Cloud Datastore Entities, Properties, and Keys + */ +public final class Blob implements java.io.Serializable { + + private static final long serialVersionUID = 3835421019618247721L; + private static final int MAX_LENGTH = 1_000_000; + + private final ByteString byteString; + + Blob(ByteString byteString, boolean enforceLimits) { + this.byteString = checkNotNull(byteString); + if (enforceLimits) { + checkArgument(byteString.size() <= MAX_LENGTH, "May be a maximum of 1,000,000 bytes"); + } + } + + @Override + public String toString() { + return byteString.toString(); + } + + @Override + public int hashCode() { + return byteString.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Blob)) { + return false; + } + return Objects.equals(byteString, ((Blob) obj).byteString); + } + + /** + * Returns the size of this blob. + */ + public int length() { + return byteString.size(); + } + + /** + * Returns a copy as byte array. + */ + public byte[] toByteArray() { + return byteString.toByteArray(); + } + + /** + * Returns a read-only {@link ByteBuffer} for this blob content. + */ + public ByteBuffer asReadOnlyByteBuffer() { + return byteString.asReadOnlyByteBuffer(); + } + + /** + * Returns an {@link InputStream} for this blob content. + */ + public InputStream asInputStream() { + final ByteBuffer byteBuffer = asReadOnlyByteBuffer(); + return new InputStream() { + @Override public int read() throws IOException { + return !byteBuffer.hasRemaining() ? -1 : byteBuffer.get() & 0xFF; + } + }; + } + + /** + * Copies bytes into a ByteBuffer. + * + * @throws java.nio.ReadOnlyBufferException if the target is read-only + * @throws java.nio.BufferOverflowException if the target's remaining() space is not large + * enough to hold the data. + */ + public void copyTo(ByteBuffer target) { + byteString.copyTo(target); + } + + /** + * Copies bytes into a buffer. + * + * @throws java.io.IndexOutOfBoundsException if an offset or size is negative or too large + */ + public void copyTo(byte[] target) { + byteString.copyTo(target, 0, 0, length()); + } + + ByteString byteString() { + return byteString; + } + + public static Blob copyFrom(byte[] bytes) { + return new Blob(ByteString.copyFrom(bytes), true); + } + + public static Blob copyFrom(ByteBuffer bytes) { + return new Blob(ByteString.copyFrom(bytes), true); + } + + public static Blob copyFrom(InputStream input) throws IOException { + BufferedInputStream bufferedInput = new BufferedInputStream(input); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + int value; + while ((value = bufferedInput.read()) != -1) { + bytes.write(value); + } + return copyFrom(bytes.toByteArray()); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/BlobValue.java b/src/main/java/com/google/gcloud/datastore/BlobValue.java new file mode 100644 index 000000000000..d44d384909a6 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/BlobValue.java @@ -0,0 +1,55 @@ +package com.google.gcloud.datastore; + +import static com.google.api.services.datastore.DatastoreV1.Value.BLOB_VALUE_FIELD_NUMBER; + +import com.google.api.services.datastore.DatastoreV1; + +public final class BlobValue extends Value { + + private static final long serialVersionUID = -5096238337676649540L; + + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { + + @Override + public int getProtoFieldId() { + return BLOB_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(Blob value) { + return new Builder(value); + } + + @Override + protected Blob getValue(DatastoreV1.Value from) { + return new Blob(from.getBlobValue(), false); + } + + @Override + protected void setValue(BlobValue from, DatastoreV1.Value.Builder to) { + to.setBlobValue(from.get().byteString()); + } + }; + + public static final class Builder extends Value.BaseBuilder { + + public Builder(Blob blob) { + super(Type.BLOB); + set(blob); + } + + @Override + public BlobValue build() { + return new BlobValue(this); + } + } + + public BlobValue(Blob blob) { + this(new Builder(blob)); + } + + BlobValue(Builder builder) { + super(builder); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/BooleanValue.java b/src/main/java/com/google/gcloud/datastore/BooleanValue.java new file mode 100644 index 000000000000..6eebdbf69b56 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/BooleanValue.java @@ -0,0 +1,55 @@ +package com.google.gcloud.datastore; + +import static com.google.api.services.datastore.DatastoreV1.Value.BOOLEAN_VALUE_FIELD_NUMBER; + +import com.google.api.services.datastore.DatastoreV1; + +public final class BooleanValue extends Value { + + private static final long serialVersionUID = -542649497897250340L; + + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { + + @Override + public int getProtoFieldId() { + return BOOLEAN_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(Boolean value) { + return new Builder(value); + } + + @Override + protected Boolean getValue(DatastoreV1.Value from) { + return from.getBooleanValue(); + } + + @Override + protected void setValue(BooleanValue from, DatastoreV1.Value.Builder to) { + to.setBooleanValue(from.get()); + } + }; + + public static final class Builder extends Value.BaseBuilder { + + public Builder(boolean value) { + super(Type.BOOLEAN); + set(value); + } + + @Override + public BooleanValue build() { + return new BooleanValue(this); + } + } + + public BooleanValue(boolean value) { + this(new Builder(value)); + } + + BooleanValue(Builder builder) { + super(builder); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/Cursor.java b/src/main/java/com/google/gcloud/datastore/Cursor.java index 51f2652b64cc..e1163da38150 100644 --- a/src/main/java/com/google/gcloud/datastore/Cursor.java +++ b/src/main/java/com/google/gcloud/datastore/Cursor.java @@ -11,11 +11,15 @@ import java.net.URLEncoder; import java.util.Arrays; +/** + * A Google Cloud Datastore cursor. + * The cursor can be used to as a starting point or an ending point for a {@link Query} + */ public final class Cursor implements Serializable { private static final long serialVersionUID = -1423744878777486541L; - private byte[] bytes; + private final byte[] bytes; public Cursor(byte[] bytes) { this.bytes = checkNotNull(bytes); @@ -40,6 +44,9 @@ public String toString() { return toPb().toString(); } + /** + * Returns the cursor in an encoded form that can be used as part of a URL. + */ public String toUrlSafe() { try { return URLEncoder.encode(toString(), UTF_8.name()); @@ -52,6 +59,9 @@ ByteString toPb() { return ByteString.copyFrom(bytes); } + /** + * Create a {@code Cursor} given its URL safe encoded form. + */ public static Cursor fromUrlSafe(String urlSafe) { try { String utf8Str = URLDecoder.decode(urlSafe, UTF_8.name()); diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java index 1843d147165e..32b4dbd73461 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java @@ -7,10 +7,25 @@ */ public interface DatastoreReader { + /** + * Returns an {@link Entity} for the given {@link Key} or {@code null} if does not exists. + * + * @throws DatastoreServiceException upon failure. + */ Entity get(Key key); - // results are returned using request order + /** + * Returns an {@link Entity} for each given {@link Key} or {@code null} if does not exists + * ordered by input. + * + * @throws DatastoreServiceException upon failure. + */ Iterator get(Key... key); + /** + * Submit a {@link Query} and returns its result. + * + * @throws DatastoreServiceException upon failure. + */ QueryResult runQuery(Query query); } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index d118b9b6c71e..1b67fbe447eb 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -3,7 +3,7 @@ import java.util.Iterator; /** - * An interface for Google Cloud Datastore. + * An interface for Google Cloud Datastore dataset. */ public interface DatastoreService extends DatastoreReader, DatastoreWriter { diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index dd6f957b92f9..fb73952613dc 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -30,20 +30,19 @@ public BatchWriter newBatchWriter(BatchWriteOption... batchWriteOption) { @Override public Key allocateId(PartialKey key) { - // TODO Auto-generated method stub - return null; + return allocateIds(key).next(); } @Override public Iterator allocateIds(PartialKey... key) { // TODO Auto-generated method stub + // Will need to populate "force" after b/18594027 is fixed. return null; } @Override public Entity get(Key key) { - // TODO Auto-generated method stub - return null; + return get(new Key[]{key}).next(); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/DateAndTime.java b/src/main/java/com/google/gcloud/datastore/DateAndTime.java new file mode 100644 index 000000000000..492802e3b8dc --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DateAndTime.java @@ -0,0 +1,74 @@ +package com.google.gcloud.datastore; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.joda.time.format.ISODateTimeFormat; + +import java.util.Calendar; +import java.util.Date; + +/** + * A Google Cloud Datastore timestamp. + * A Datastore timestamp is represented in micro-seconds. + * This class is immutable. + * + * @see Google Cloud Datastore Entities, Properties, and Keys + */ +public final class DateAndTime implements java.io.Serializable { + + private static final long serialVersionUID = 7343324797621228378L; + + private final long timestampMicroseconds; + + DateAndTime(long timestampMicroseconds) { + this.timestampMicroseconds = timestampMicroseconds; + } + + @Override + public String toString() { + return ISODateTimeFormat.dateTime().print(timestampMillis()); + } + + @Override + public int hashCode() { + return (int) timestampMicroseconds; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DateAndTime)) { + return false; + } + return timestampMicroseconds == ((DateAndTime) obj).timestampMicroseconds; + } + + public long timestampMicroseconds() { + return timestampMicroseconds; + } + + public long timestampMillis() { + return timestampMicroseconds / 1000L; + } + + public Date toDate() { + return new Date(timestampMillis()); + } + + public Calendar toCalendar() { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(timestampMillis()); + return cal; + } + + public static DateAndTime now() { + return new DateAndTime(System.nanoTime() / 1000L); + } + + public static DateAndTime copyFrom(Date date) { + return new DateAndTime(checkNotNull(date).getTime() * 1000L); + } + + public static DateAndTime copyFrom(Calendar calendar) { + return copyFrom(calendar.getTime()); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/DateAndTimeValue.java b/src/main/java/com/google/gcloud/datastore/DateAndTimeValue.java new file mode 100644 index 000000000000..e1fa416c6bb1 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DateAndTimeValue.java @@ -0,0 +1,57 @@ +package com.google.gcloud.datastore; + +import static com.google.api.services.datastore.DatastoreV1.Value.TIMESTAMP_MICROSECONDS_VALUE_FIELD_NUMBER; + +import com.google.api.services.datastore.DatastoreV1; + +public final class DateAndTimeValue + extends Value { + + private static final long serialVersionUID = -5096238337676649540L; + + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { + + @Override + public int getProtoFieldId() { + return TIMESTAMP_MICROSECONDS_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(DateAndTime value) { + return new Builder(value); + } + + @Override + protected DateAndTime getValue(DatastoreV1.Value from) { + return new DateAndTime(from.getTimestampMicrosecondsValue()); + } + + @Override + protected void setValue(DateAndTimeValue from, DatastoreV1.Value.Builder to) { + to.setTimestampMicrosecondsValue(from.get().timestampMicroseconds()); + } + }; + + public static final class Builder + extends Value.BaseBuilder { + + public Builder(DateAndTime dateAndTime) { + super(Type.DATE_AND_TIME); + set(dateAndTime); + } + + @Override + public DateAndTimeValue build() { + return new DateAndTimeValue(this); + } + } + + public DateAndTimeValue(DateAndTime dateAndTime) { + this(new Builder(dateAndTime)); + } + + DateAndTimeValue(Builder builder) { + super(builder); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/DoubleValue.java b/src/main/java/com/google/gcloud/datastore/DoubleValue.java new file mode 100644 index 000000000000..dca443a32851 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DoubleValue.java @@ -0,0 +1,55 @@ +package com.google.gcloud.datastore; + +import static com.google.api.services.datastore.DatastoreV1.Value.DOUBLE_VALUE_FIELD_NUMBER; + +import com.google.api.services.datastore.DatastoreV1; + +public final class DoubleValue extends Value { + + private static final long serialVersionUID = -5096238337676649540L; + + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { + + @Override + public int getProtoFieldId() { + return DOUBLE_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(Double value) { + return new Builder(value); + } + + @Override + protected Double getValue(DatastoreV1.Value from) { + return from.getDoubleValue(); + } + + @Override + protected void setValue(DoubleValue from, DatastoreV1.Value.Builder to) { + to.setDoubleValue(from.get()); + } + }; + + public static final class Builder extends Value.BaseBuilder { + + public Builder(double value) { + super(Type.DOUBLE); + set(value); + } + + @Override + public DoubleValue build() { + return new DoubleValue(this); + } + } + + public DoubleValue(double value) { + this(new Builder(value)); + } + + DoubleValue(Builder builder) { + super(builder); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/Entity.java b/src/main/java/com/google/gcloud/datastore/Entity.java index b1241a16ed16..2bdd2dbfe41b 100644 --- a/src/main/java/com/google/gcloud/datastore/Entity.java +++ b/src/main/java/com/google/gcloud/datastore/Entity.java @@ -1,47 +1,55 @@ package com.google.gcloud.datastore; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSortedMap; import com.google.protobuf.InvalidProtocolBufferException; +/** + * An entity is the Google Cloud Datastore persistent data object. + * An entity holds one or more properties, represented by a name (as {@link String}) + * and a value (as {@link Value}), and is associated with a {@link Key}. + * For a list of possible values see {@link Value.Type}. + * This class is immutable. To edit (a copy) use {@link #builder()}. + * + * @see Google Cloud Datastore Entities, Properties, and Keys + */ public final class Entity extends PartialEntity { private static final long serialVersionUID = 432961565733066915L; - public static final class Builder { - - private PartialEntity.Builder delegate; + public static final class Builder extends PartialEntity.Builder { public Builder(Key key) { - delegate = new PartialEntity.Builder(); - delegate.setKey(checkNotNull(key)); + super(key); } public Builder(Entity entity) { - delegate = new PartialEntity.Builder(entity); + super(entity); } + @Override public Builder clearProperties() { - delegate.clearProperties(); + super.clearProperties(); return this; } + @Override public Builder removeProperty(String name) { - delegate.removeProperty(name); + super.removeProperty(name); return this; } + @Override public Builder setProperty(String name, Value value) { - delegate.setProperty(name, value); + super.setProperty(name, value); return this; } + @Override public Entity build() { - PartialEntity entity = delegate.build(); - return new Entity((Key) entity.getKey(), entity.getProperties()); + PartialEntity entity = super.build(); + return new Entity((Key) entity.key(), entity.properties()); } } @@ -53,8 +61,13 @@ private Entity(Key key, ImmutableSortedMap> properties) { * Returns the entity's key (never null). */ @Override - public Key getKey() { - return (Key) super.getKey(); + public Key key() { + return (Key) super.key(); + } + + @Override + public Builder builder() { + return new Builder(this); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java index b99ca22ad226..2f8f87ba0f18 100644 --- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java +++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java @@ -3,7 +3,7 @@ import com.google.api.services.datastore.DatastoreV1; import com.google.protobuf.InvalidProtocolBufferException; -public class GqlQuery extends Query { +public class GqlQuery extends Serializable implements Query { private static final long serialVersionUID = 5988280590929540569L; diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index bc145a157b7b..33f013e8ad3f 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -1,6 +1,5 @@ package com.google.gcloud.datastore; -import static com.google.common.base.Preconditions.checkNotNull; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.api.services.datastore.DatastoreV1; @@ -8,108 +7,154 @@ import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import java.io.ObjectStreamException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; /** - * A key that is guaranteed to be complete. + * A key that is guaranteed to be complete and could be used to reference a + * Google Cloud Datastore {@link Entity}. + * This class is immutable. To edit (a copy) use {@link #builder()}. + * + * @see Google Cloud Datastore Entities, Properties, and Keys */ public final class Key extends PartialKey { private static final long serialVersionUID = 3160994559785491356L; - public static class Builder { + public static final class Builder extends PartialKey.Builder { - private final PartialKey.Builder delegate; private String name; private Long id; public Builder(String dataset, String kind, String name) { - delegate = new PartialKey.Builder(dataset, kind); + super(dataset, kind); this.name = name; } public Builder(String dataset, String kind, long id) { - delegate = new PartialKey.Builder(dataset, kind); + super(dataset, kind); this.id = id; } public Builder(PartialKey key, String name) { - delegate = new PartialKey.Builder(key); + super(key); this.name = name; } public Builder(PartialKey key, long id) { - delegate = new PartialKey.Builder(key); + super(key); this.id = id; } - public Builder setDataset(String dataset) { - delegate.setDataset(checkNotNull(dataset)); + public Builder(Key key) { + super(key); + if (key.id() == null) { + name = key.name(); + } else { + id = key.id(); + } + } + + @Override + public Builder addToPath(String kind, long id) { + super.addToPath(kind, id); return this; } - public Builder setNamespace(String namespace) { - delegate.setNamespace(checkNotNull(namespace)); + @Override + public Builder addToPath(String kind, String name) { + super.addToPath(kind, name); return this; } - public Builder setName(String name) { - this.name = name; - this.id = null; + @Override + public Builder addToPath(PathElement pathElement) { + super.addToPath(pathElement); return this; } - public Builder setId(long id) { - this.id = id; - this.name = null; + @Override + public Builder kind(String kind) { + super.kind(kind); return this; } - public Builder addToPath(String kind, long id) { - delegate.addToPath(kind, id); + @Override + public Builder clearPath() { + super.clearPath(); return this; } - public Builder addToPath(String kind, String name) { - delegate.addToPath(kind, name); + @Override + public Builder dataset(String dataset) { + super.dataset(dataset); return this; } - public Builder clearPath() { - delegate.clearPath(); + @Override + public Builder namespace(String namespace) { + super.namespace(namespace); return this; } + public Builder name(String name) { + this.name = name; + id = null; + return this; + } + + public Builder id(long id) { + this.id = id; + name = null; + return this; + } + + @Override public Key build() { - PartialKey key = delegate.build(); + PartialKey key = super.build(); return id == null ? new Key(key, name) : new Key(key, id); } } private Key(PartialKey key, String name) { - super(key.getDataset(), key.getNamespace(), newPath(key, name)); + super(key.dataset(), key.namespace(), newPath(key, name)); } private Key(PartialKey key, long id) { - super(key.getDataset(), key.getNamespace(), newPath(key, id)); + super(key.dataset(), key.namespace(), newPath(key, id)); } - public Long getId() { - return getLeaf().getId(); + @Override + public Builder builder() { + return new Builder(this); } - public String getName() { - return getLeaf().getName(); + /** + * Returns the key's id or {@code null} if it has a name instead. + */ + public Long id() { + return getLeaf().id(); } - public Object getNameOrId() { + /** + * Returns the key's name or {@code null} if it has an id instead. + */ + public String name() { + return getLeaf().name(); + } + + /** + * Returns the key's id (as {@link #Long}) or name (as {@link String}). + */ + public Object nameOrId() { PathElement leaf = getLeaf(); - return leaf.hasId() ? leaf.getId() : leaf.getName(); + return leaf.hasId() ? leaf.id() : leaf.name(); } + /** + * Returns the key in an encoded form that can be used as part of a URL. + */ public String toUrlSafe() { try { return URLEncoder.encode(toString(), UTF_8.name()); @@ -118,6 +163,11 @@ public String toUrlSafe() { } } + /** + * Create a {@code Key} given its URL safe encoded form. + * + * @throws RuntimeException when decoding fails + */ public static Key fromUrlSafe(String urlSafe) { try { String utf8Str = URLDecoder.decode(urlSafe, UTF_8.name()); @@ -142,33 +192,33 @@ public static Key fromIncompleteKey(PartialKey key) { } PathElement leaf = key.getLeaf(); if (leaf.hasId()) { - return new Key(key, leaf.getId()); + return new Key(key, leaf.id()); } else if (leaf.hasName()) { - return new Key(key, leaf.getName()); + return new Key(key, leaf.name()); } throw new IllegalArgumentException("Key is missing name or id"); } - static Key fromPb(DatastoreV1.Key keyPb) { - return fromIncompleteKey(PartialKey.fromPb(keyPb)); + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + return fromPb(DatastoreV1.Key.parseFrom(bytesPb)); } - @Override - protected Object readResolve() throws ObjectStreamException { - return fromIncompleteKey((PartialKey) super.readResolve()); + static Key fromPb(DatastoreV1.Key keyPb) { + return fromIncompleteKey(PartialKey.fromPb(keyPb)); } private static ImmutableList newPath(PartialKey key, String name) { ImmutableList.Builder path = ImmutableList.builder(); - path.addAll(key.getAncestorPath()); - path.add(new PathElement(key.getKind(), name)); + path.addAll(key.ancestorPath()); + path.add(new PathElement(key.kind(), name)); return path.build(); } private static ImmutableList newPath(PartialKey key, long id) { ImmutableList.Builder path = ImmutableList.builder(); - path.addAll(key.getAncestorPath()); - path.add(new PathElement(key.getKind(), id)); + path.addAll(key.ancestorPath()); + path.add(new PathElement(key.kind(), id)); return path.build(); } } diff --git a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java index 78f6fc1790de..07c6382f0dc5 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java +++ b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java @@ -3,7 +3,7 @@ import static com.google.common.base.Preconditions.checkNotNull; /** - * An helper for creating keys for a {@link DatastoreService}. + * An helper for creating keys for a specific {@link DatastoreService}/dataset. */ public final class KeyBuilder { @@ -16,7 +16,7 @@ public final class KeyBuilder { public KeyBuilder(DatastoreService service, String kind) { this.service = checkNotNull(service); delegate = new PartialKey.Builder(service.getOptions().getDataset(), kind); - delegate.setNamespace(service.getOptions().getDefaultNamespace()); + delegate.namespace(service.getOptions().getDefaultNamespace()); } public KeyBuilder addToPath(String kind, String name) { @@ -29,8 +29,8 @@ public KeyBuilder addToPath(String kind, long id) { return this; } - public KeyBuilder setNamespace(String namespace) { - delegate.setNamespace(namespace); + public KeyBuilder namespace(String namespace) { + delegate.namespace(namespace); return this; } diff --git a/src/main/java/com/google/gcloud/datastore/KeyValue.java b/src/main/java/com/google/gcloud/datastore/KeyValue.java index 637fb6931bdb..671e9c355b14 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyValue.java +++ b/src/main/java/com/google/gcloud/datastore/KeyValue.java @@ -34,9 +34,9 @@ protected void setValue(KeyValue from, DatastoreV1.Value.Builder to) { public static final class Builder extends Value.BaseBuilder { - public Builder(Key value) { + public Builder(Key key) { super(Type.KEY); - set(value); + set(key); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/ListValue.java b/src/main/java/com/google/gcloud/datastore/ListValue.java index b33c096ff718..b8bdb7ec17d6 100644 --- a/src/main/java/com/google/gcloud/datastore/ListValue.java +++ b/src/main/java/com/google/gcloud/datastore/ListValue.java @@ -55,7 +55,7 @@ public Builder() { } public Builder addValue(Value value) { - Preconditions.checkArgument(value.getType() != Type.LIST, "Cannot contain another list"); + Preconditions.checkArgument(value.type() != Type.LIST, "Cannot contain another list"); listBuilder.add(value); return this; } @@ -68,6 +68,11 @@ public Builder addValue(Value first, Value... other) { return this; } + /** + * Copy the list of values. + * + * @see com.google.gcloud.datastore.Value.BaseBuilder#set(java.lang.Object) + */ @Override public Builder set(List> properties) { listBuilder = ImmutableList.>builder(); diff --git a/src/main/java/com/google/gcloud/datastore/LongValue.java b/src/main/java/com/google/gcloud/datastore/LongValue.java new file mode 100644 index 000000000000..9a4690a24133 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/LongValue.java @@ -0,0 +1,55 @@ +package com.google.gcloud.datastore; + +import static com.google.api.services.datastore.DatastoreV1.Value.INTEGER_VALUE_FIELD_NUMBER; + +import com.google.api.services.datastore.DatastoreV1; + +public final class LongValue extends Value { + + private static final long serialVersionUID = -8552854340400546861L; + + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { + + @Override + public int getProtoFieldId() { + return INTEGER_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(Long value) { + return new Builder(value); + } + + @Override + protected Long getValue(DatastoreV1.Value from) { + return from.getIntegerValue(); + } + + @Override + protected void setValue(LongValue from, DatastoreV1.Value.Builder to) { + to.setIntegerValue(from.get()); + } + }; + + public static final class Builder extends Value.BaseBuilder { + + public Builder(long value) { + super(Type.LONG); + set(value); + } + + @Override + public LongValue build() { + return new LongValue(this); + } + } + + public LongValue(long value) { + this(new Builder(value)); + } + + LongValue(Builder builder) { + super(builder); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java index d97e168c27c6..c13ea5679b64 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java @@ -9,6 +9,13 @@ import java.util.Objects; import java.util.Set; +/** + * A partial entity holds one or more properties, represented by a name (as {@link String}) + * and a value (as {@link Value}). + * For a list of possible values see {@link Value.Type}. + * A partial entity also can be associated with a key (partial or full). + * This class is immutable. To edit (a copy) use {@link #builder()}. + */ public class PartialEntity extends Serializable { private static final long serialVersionUID = 6492561268709192891L; @@ -16,23 +23,22 @@ public class PartialEntity extends Serializable { private final transient PartialKey key; private final transient ImmutableSortedMap> properties; - public static final class Builder { + public static class Builder { - private PartialKey key; - private Map> properties; + private final PartialKey key; + private final Map> properties; - public Builder() { + /** + * Construct a builder with a partial key (could be null). + */ + public Builder(PartialKey key) { + this.key = key; properties = new HashMap<>(); } public Builder(PartialEntity entity) { - this.key = entity.getKey(); - this.properties = new HashMap<>(entity.getProperties()); - } - - public Builder setKey(PartialKey key) { - this.key = key; - return this; + key = entity.key(); + properties = new HashMap<>(entity.properties()); } public Builder clearProperties() { @@ -61,9 +67,9 @@ protected PartialEntity(PartialKey key, ImmutableSortedMap getProperty(String name) { - return properties.get(name); + @SuppressWarnings("unchecked") + public , B extends Value.Builder> Value property( + String name) { + return (Value) properties.get(name); } - public Set getPropertyNames() { + public Set propertyNames() { return properties.keySet(); } - ImmutableSortedMap> getProperties() { + /** + * Returns a new builder for this entity (values are copied). + */ + public Builder builder() { + return new Builder(this); + } + + ImmutableSortedMap> properties() { return properties; } @@ -119,10 +134,8 @@ protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { } static PartialEntity fromPb(DatastoreV1.Entity entityPb) { - Builder builder = new Builder(); - if (entityPb.hasKey()) { - builder.setKey(PartialKey.fromPb(entityPb.getKey())); - } + PartialKey key = entityPb.hasKey() ? PartialKey.fromPb(entityPb.getKey()) : null; + Builder builder = new Builder(key); for (DatastoreV1.Property property : entityPb.getPropertyList()) { builder.setProperty(property.getName(), Value.fromPb(property.getValue())); } diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java b/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java index db076a3305a0..cc4dd834a343 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java @@ -5,7 +5,7 @@ import com.google.api.services.datastore.DatastoreV1; public class PartialEntityValue extends - Value { +Value { private static final long serialVersionUID = -5461475706792576395L; @@ -34,7 +34,7 @@ protected void setValue(PartialEntityValue from, DatastoreV1.Value.Builder to) { }; public static final class Builder extends - Value.BaseBuilder { + Value.BaseBuilder { public Builder(PartialEntity entity) { super(Type.PARTIAL_ENTITY); diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index 261e8b2b3f6b..4bcca81ddb64 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -15,6 +15,11 @@ import java.util.List; import java.util.Objects; +/** + * A partial key (without a name or id). + * Could be used as metadata for {@link PartialEntity}. + * This class is immutable. To edit (a copy) use {@link #builder()}. + */ public class PartialKey extends Serializable { private static final long serialVersionUID = -75301206578793347L; @@ -38,16 +43,16 @@ private PathElement(String kind) { public PathElement(String kind, long id) { this.kind = kind; this.id = id; - this.name = null; + name = null; } public PathElement(String kind, String name) { this.kind = kind; this.name = name; - this.id = null; + id = null; } - public String getKind() { + public String kind() { return kind; } @@ -55,7 +60,7 @@ public boolean hasId() { return id != null; } - public long getId() { + public long id() { return id == null ? 0 : id; } @@ -63,11 +68,11 @@ public boolean hasName() { return name != null; } - public String getName() { + public String name() { return name == null ? "" : name; } - public Object getNameOrId() { + public Object nameOrId() { return id == null ? name : id; } @@ -115,12 +120,12 @@ static PathElement fromPb(DatastoreV1.Key.PathElement pathElementPb) { } } - public static final class Builder { + public static class Builder { private String dataset; private String namespace = DEFAULT_NAMESPACE; private String kind; - private List path = new LinkedList<>(); + private final List path = new LinkedList<>(); private static final String DEFAULT_NAMESPACE = ""; private static final int MAX_PATH = 100; @@ -133,8 +138,8 @@ public Builder(String dataset, String kind) { public Builder(PartialKey key) { dataset = key.dataset; namespace = key.namespace; - kind = key.getKind(); - path.addAll(key.getAncestorPath()); + kind = key.kind(); + path.addAll(key.ancestorPath()); } public Builder addToPath(String kind, long id) { @@ -148,13 +153,13 @@ public Builder addToPath(String kind, String name) { return addToPath(new PathElement(kind, name)); } - public Builder addToPath(PathElement pathEntry) { + public Builder addToPath(PathElement pathElement) { Preconditions.checkState(path.size() < MAX_PATH, "path can have at most 100 elements"); - path.add(pathEntry); + path.add(pathElement); return this; } - public Builder setKind(String kind) { + public Builder kind(String kind) { this.kind = validateKind(kind); return this; } @@ -170,12 +175,12 @@ public Builder clearPath() { return this; } - public Builder setDataset(String dataset) { + public Builder dataset(String dataset) { this.dataset = validateDataset(dataset); return this; } - public Builder setNamespace(String namespace) { + public Builder namespace(String namespace) { this.namespace = checkNotNull(namespace); return this; } @@ -195,20 +200,36 @@ public PartialKey build() { this.path = path; } - public String getDataset() { + /** + * Returns the key's dataset. + */ + public String dataset() { return dataset; } - public String getNamespace() { + /** + * Returns the key's namespace. + */ + public String namespace() { return namespace; } - public List getAncestorPath() { + /** + * Returns the key's parent's path. + */ + public List ancestorPath() { return path.subList(0, path.size() - 1); } - public String getKind() { - return getLeaf().getKind(); + /** + * Returns the key's kind. + */ + public String kind() { + return getLeaf().kind(); + } + + public Builder builder() { + return new Builder(this); } @Override @@ -217,14 +238,14 @@ public int hashCode() { } @Override - public boolean equals(Object other) { - if (!(other instanceof PartialKey)) { + public boolean equals(Object obj) { + if (!(obj instanceof PartialKey)) { return false; } - PartialKey otherKey = (PartialKey) other; - return Objects.equals(dataset, otherKey.dataset) - && Objects.equals(namespace, otherKey.namespace) - && Objects.equals(path, otherKey.path); + PartialKey other = (PartialKey) obj; + return Objects.equals(dataset, other.dataset) + && Objects.equals(namespace, other.namespace) + && Objects.equals(path, other.path); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/Query.java b/src/main/java/com/google/gcloud/datastore/Query.java index cff0b13aaf73..822b7c6dc472 100644 --- a/src/main/java/com/google/gcloud/datastore/Query.java +++ b/src/main/java/com/google/gcloud/datastore/Query.java @@ -1,8 +1,7 @@ package com.google.gcloud.datastore; -import com.google.protobuf.GeneratedMessage; +import java.io.Serializable; -public abstract class Query extends Serializable { - private static final long serialVersionUID = 4960655852209261775L; +public interface Query extends Serializable { } diff --git a/src/main/java/com/google/gcloud/datastore/QueryResult.java b/src/main/java/com/google/gcloud/datastore/QueryResult.java index d47edc484ddd..446fc6dc43e4 100644 --- a/src/main/java/com/google/gcloud/datastore/QueryResult.java +++ b/src/main/java/com/google/gcloud/datastore/QueryResult.java @@ -3,31 +3,46 @@ import java.util.Iterator; /** - * A result of Google Cloud Datastore query. + * The result of a Google Cloud Datastore query submission. + * Typically the result's type would be casted to its expected type (The {@link #getResultType()} + * method could be used when the type is not known). + * + * @param V the type of values the result holds. */ public interface QueryResult extends Iterator { /** - * The type of the result. - * Full: A complete {@link Entity}. + * The result's type. + * FULL: A complete {@link Entity}. * PROJECTION: A partial entity, represented by {@link PartialEntity}. * KEY_ONLY: An entity's {@link Key}. */ - enum ResultType { - FULL, - PROJECTION, - KEY_ONLY; + enum Type { + + FULL(Entity.class), + PROJECTION(PartialEntity.class), + KEY_ONLY(Key.class); + + private final Class resultClass; + + Type(Class resultClass) { + this.resultClass = resultClass; + } + + Class getResultClass() { + return resultClass; + } } /** - * This method should be used to cast the result to its appropriate type. + * This method can be used to verify the result type and to cast its value type accordingly. *
 {@code
-   * ResultType.FULL -> (QueryResult)
-   * ResultType.PROJECTION -> (QueryResult)
-   * ResultType.KEY_ONLY -> (QueryResult)
+   * Type.FULL -> (QueryResult)
+   * Type.PROJECTION -> (QueryResult)
+   * Type.KEY_ONLY -> (QueryResult)
    * } 
*/ - ResultType getResultType(); + Type getType(); /** * Return the Cursor for the next result. Not currently implemented (depends on v1beta3). diff --git a/src/main/java/com/google/gcloud/datastore/RawValue.java b/src/main/java/com/google/gcloud/datastore/RawValue.java new file mode 100644 index 000000000000..120ad7085e4e --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/RawValue.java @@ -0,0 +1,59 @@ +package com.google.gcloud.datastore; + +import com.google.api.services.datastore.DatastoreV1; + +public final class RawValue extends Value { + + private static final long serialVersionUID = -3359604598651897941L; + + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { + + @Override + public Builder newBuilder(DatastoreV1.Value value) { + return new Builder(value); + } + + @Override + public int getProtoFieldId() { + return 0; + } + + @Override + protected DatastoreV1.Value getValue(DatastoreV1.Value from) { + return from; + } + + @Override + protected void setValue(RawValue from, DatastoreV1.Value.Builder to) { + to.mergeFrom(from.get()); + } + }; + + static final class Builder extends Value.BaseBuilder { + + Builder(DatastoreV1.Value valuePb) { + super(Type.RAW_VALUE); + if (valuePb.hasIndexed()) { + indexed(valuePb.getIndexed()); + } + if (valuePb.hasMeaning()) { + meaning(valuePb.getMeaning()); + } + set(valuePb); + } + + @Override + public RawValue build() { + return new RawValue(this); + } + } + + RawValue(Builder builder) { + super(builder); + } + + RawValue(DatastoreV1.Value valuePb) { + this(new Builder(valuePb)); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/Serializable.java b/src/main/java/com/google/gcloud/datastore/Serializable.java index c9f6b7984512..e9964b6d0e4a 100644 --- a/src/main/java/com/google/gcloud/datastore/Serializable.java +++ b/src/main/java/com/google/gcloud/datastore/Serializable.java @@ -15,6 +15,23 @@ abstract class Serializable implements java.io.Seria private transient byte[] bytesPb; // only for deserialization + + @Override + public int hashCode() { + return toPb().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!getClass().isInstance(obj)) { + return false; + } + return toPb().equals(((Serializable) obj).toPb()); + } + @Override public String toString() { return toPb().toString(); diff --git a/src/main/java/com/google/gcloud/datastore/StringValue.java b/src/main/java/com/google/gcloud/datastore/StringValue.java index 70d77e250782..7f8ee6e979d8 100644 --- a/src/main/java/com/google/gcloud/datastore/StringValue.java +++ b/src/main/java/com/google/gcloud/datastore/StringValue.java @@ -42,15 +42,15 @@ public Builder(String value) { @Override public StringValue build() { - if (getIndexed()) { + if (Boolean.TRUE.equals(getIndexed())) { checkArgument(get().length() <= 500, "Indexed string is limited to 500 characters"); } return new StringValue(this); } } - public StringValue(String content) { - this(new Builder(content)); + public StringValue(String value) { + this(new Builder(value)); } StringValue(Builder builder) { diff --git a/src/main/java/com/google/gcloud/datastore/Transaction.java b/src/main/java/com/google/gcloud/datastore/Transaction.java index 4df12134c3ed..fd916960d78a 100644 --- a/src/main/java/com/google/gcloud/datastore/Transaction.java +++ b/src/main/java/com/google/gcloud/datastore/Transaction.java @@ -2,10 +2,20 @@ /** * A Google cloud datastore transaction. + * + * @see Google Cloud Datastore transactions */ public interface Transaction extends DatastoreReader, DatastoreWriter { + /** + * Commit the transaction. + * + * @throws DatastoreServiceException if could not commit the transaction + */ void commit(); + /** + * Rollback the transaction. + */ void rollback(); } diff --git a/src/main/java/com/google/gcloud/datastore/Value.java b/src/main/java/com/google/gcloud/datastore/Value.java index edc197ddd696..3d24423b65d8 100644 --- a/src/main/java/com/google/gcloud/datastore/Value.java +++ b/src/main/java/com/google/gcloud/datastore/Value.java @@ -4,52 +4,111 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.api.services.datastore.DatastoreV1; +import com.google.common.base.MoreObjects; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.InvalidProtocolBufferException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; -// TODO: add javadoc, and mention that null should only be represented by NullValue. +/** + * Base class for all Google Cloud Datastore value types. + * All values must be associated with a non-null content (except {@link NullValue}). + * All values are immutable (including their content). To edit (a copy) use {@link #builder()}. + * Unsupported value (deprecated or unrecognized) would be represented by {@link RawValue}. + * + * @param the type of the content for this value + * @param

the type of this value + * @param the type of this value's builder + */ public abstract class Value, B extends Value.Builder> extends Serializable { private static final long serialVersionUID = -1899638277588872742L; + private static final Map DESCRIPTOR_TO_TYPE_MAP = new HashMap<>(); private final transient Type type; - private final transient boolean indexed; + private final transient Boolean indexed; private final transient Integer meaning; private final transient V value; + /** + * The type of a property. + * + * @see Google Cloud Datastore types + */ public enum Type { + /** + * Represents a {@code null} value. + */ NULL(NullValue.MARSHALLER, NullValue.MARSHALLER), + + /** + * Represents a {@code string} value. + */ STRING(StringValue.MARSHALLER, StringValue.MARSHALLER), + + /** + * Represents an {@link PartialEntity} value. + */ PARTIAL_ENTITY(PartialEntityValue.MARSHALLER, PartialEntityValue.MARSHALLER), + + /** + * Represents a {@code list} of {@link Value}s. + */ LIST(ListValue.MARSHALLER, ListValue.MARSHALLER), - KEY(KeyValue.MARSHALLER, KeyValue.MARSHALLER); - - /* - TODO: Also implement - LONG(LongValue.class, INTEGER_VALUE_FIELD_NUMBER), - DOUBLE(DoubleValue.class, DOUBLE_VALUE_FIELD_NUMBER), - // TODO: make sure that getContent returns an immutable value or at least a copy - TIMESTAMP(TimestampValue.class, TIMESTAMP_MICROSECONDS_VALUE_FIELD_NUMBER), - BOOLEAN(BooleanValue.class, BOOLEAN_VALUE_FIELD_NUMBER), - BLOB(BlobValue.class, BLOB_VALUE_FIELD_NUMBER), - BLOB_KEY(BlobKeyValue.class, BLOB_KEY_VALUE_FIELD_NUMBER), - // GEO_POINT(GeoPointValue.class, 8) // Does not seem to be public yet... + + /** + * Represents a {@code key} as a value. */ + KEY(KeyValue.MARSHALLER, KeyValue.MARSHALLER), + + /** + * Represents a {@code long} value. + */ + LONG(LongValue.MARSHALLER, LongValue.MARSHALLER), + + /** + * Represents a {@code double} value. + */ + DOUBLE(DoubleValue.MARSHALLER, DoubleValue.MARSHALLER), + + /** + * Represents a {@code boolean} value. + */ + BOOLEAN(BooleanValue.MARSHALLER, BooleanValue.MARSHALLER), + + /** + * Represents a {@link DateAndTime} value. + */ + DATE_AND_TIME(DateAndTimeValue.MARSHALLER, DateAndTimeValue.MARSHALLER), + + /** + * Represents a {@link Blob} value. + */ + BLOB(BlobValue.MARSHALLER, BlobValue.MARSHALLER), + + /** + * Represents a raw/unparsed value. + */ + RAW_VALUE(RawValue.MARSHALLER, RawValue.MARSHALLER); + @SuppressWarnings("rawtypes") private final BuilderFactory builderFactory; @SuppressWarnings("rawtypes") private final Marshaller marshaller; - private FieldDescriptor field; , B extends Builder> Type(Marshaller marshaller, BuilderFactory builderFactory) { this.marshaller = marshaller; this.builderFactory = builderFactory; - field = DatastoreV1.Value.getDescriptor().findFieldByNumber(marshaller.getProtoFieldId()); + int fieldId = marshaller.getProtoFieldId(); + if (fieldId > 0) { + DESCRIPTOR_TO_TYPE_MAP.put(fieldId, this); + } } , B extends Builder> Marshaller @@ -61,10 +120,6 @@ public enum Type { getBuilderFactory() { return builderFactory; } - - FieldDescriptor getDescriptor() { - return field; - } } interface BuilderFactory, B extends Builder> { @@ -78,7 +133,7 @@ interface Builder, B extends Builder> { B mergeFrom(P other); - boolean getIndexed(); + Boolean getIndexed(); B indexed(boolean indexed); @@ -108,7 +163,9 @@ abstract static class BaseMarshaller, B extends Buil @Override public final B fromProto(DatastoreV1.Value proto) { B builder = newBuilder(getValue(proto)); - builder.indexed(proto.getIndexed()); + if (proto.hasIndexed()) { + builder.indexed(proto.getIndexed()); + } if (proto.hasMeaning()) { builder.meaning(proto.getMeaning()); } @@ -118,9 +175,11 @@ public final B fromProto(DatastoreV1.Value proto) { @Override public final DatastoreV1.Value toProto(P value) { DatastoreV1.Value.Builder builder = DatastoreV1.Value.newBuilder(); - builder.setIndexed(value.getIndexed()); - if (value.getMeaning() != null) { - builder.setMeaning(value.getMeaning()); + if (value.hasIndexed()) { + builder.setIndexed(value.indexed()); + } + if (value.hasMeaning()) { + builder.setMeaning(value.meaning()); } setValue(value, builder); return builder.build(); @@ -135,7 +194,7 @@ abstract static class BaseBuilder, B extends BaseBui implements Builder { private final Type type; - private boolean indexed = true; + private Boolean indexed; private Integer meaning; private V value; @@ -150,14 +209,14 @@ public Type getType() { @Override public B mergeFrom(P other) { - indexed = other.getIndexed(); - meaning = other.getMeaning(); + indexed = other.indexed(); + meaning = other.meaning(); set(other.get()); return self(); } @Override - public boolean getIndexed() { + public Boolean getIndexed() { return indexed; } @@ -203,27 +262,34 @@ private B self() { indexed = builder.getIndexed(); meaning = builder.getMeaning(); // some validations: - if (meaning != null) { + if (meaning != null && indexed != null) { // TODO: consider supplying Ranges for allowed meaning and validating it here // more specific validation could be done on the specific types themselves // upon construction [e.g. integer with a meaning 13 should be in the range [0,100]] - if (indexed) { - checkArgument(meaning != 15 && meaning != 22, - "Indexed values should not have meaning with 15 or 22"); - } + checkArgument(!indexed || meaning != 15 && meaning != 22, + "Indexed values should not have meaning with 15 or 22"); } value = builder.get(); } - public final Type getType() { + public final Type type() { return type; } - public final boolean getIndexed() { - return indexed; + public final boolean hasIndexed() { + return indexed != null; + } + + public final boolean indexed() { + // default indexed value is true + return MoreObjects.firstNonNull(indexed, Boolean.TRUE); } - public final Integer getMeaning() { + public final boolean hasMeaning() { + return meaning != null; + } + + public final Integer meaning() { return meaning; } @@ -232,8 +298,8 @@ public final V get() { } @SuppressWarnings("unchecked") - public final B toBuilder() { - BuilderFactory builderFactory = getType().getBuilderFactory(); + public final B builder() { + BuilderFactory builderFactory = type().getBuilderFactory(); B builder = builderFactory.newBuilder(get()); return builder.mergeFrom((P) this); } @@ -245,48 +311,40 @@ public int hashCode() { @SuppressWarnings("unchecked") @Override - public boolean equals(Object other) { - if (!getClass().isInstance(other)) { + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!getClass().isInstance(obj)) { return false; } - - Value otherValue = (Value) other; - return Objects.equals(type, otherValue.getType()) - && Objects.equals(indexed, otherValue.getIndexed()) - && Objects.equals(meaning, otherValue.getMeaning()) - && Objects.equals(value, otherValue.get()); + Value other = (Value) obj; + return Objects.equals(type, other.type) + && Objects.equals(indexed, other.indexed) + && Objects.equals(meaning, other.meaning) + && Objects.equals(value, other.value); } @Override @SuppressWarnings("unchecked") protected DatastoreV1.Value toPb() { - Marshaller marshaller = getType().getMarshaller(); + Marshaller marshaller = type().getMarshaller(); return marshaller.toProto((P) this); } - @SuppressWarnings({ "rawtypes", "unchecked" }) - static , B extends Value.Builder> Value - fromPb(DatastoreV1.Value proto) { - Marshaller marshaller = NullValue.MARSHALLER; - for (Type type : Type.values()) { - FieldDescriptor descriptor = type.getDescriptor(); - if (descriptor != null) { - if (descriptor.isRepeated()) { - if (proto.getRepeatedFieldCount(descriptor) > 0) { - marshaller = type.getMarshaller(); - break; - } - } else if (proto.hasField(descriptor)) { - marshaller = type.getMarshaller(); - break; + static Value fromPb(DatastoreV1.Value proto) { + for (Entry entry : proto.getAllFields().entrySet()) { + FieldDescriptor descriptor = entry.getKey(); + if (descriptor.getName().endsWith("_value")) { + Type type = DESCRIPTOR_TO_TYPE_MAP.get(descriptor.getNumber()); + if (type == null) { + // unsupported type + return RawValue.MARSHALLER.fromProto(proto).build(); } + return type.getMarshaller().fromProto(proto).build(); } } - // change strategy to return RawValue (package scope constructor) - // when no match instead of null. This could only be done - // when using the V3 API which added a NullValue type to distinct the cases - // and the use of oneof which generates an enum of all possible values. - return marshaller.fromProto(proto).build(); + return NullValue.MARSHALLER.fromProto(proto).build(); } @Override diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index 7f4f497dd970..ab8e839c15b2 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -1,6 +1,6 @@ package com.google.gcloud.datastore; -import static org.junit.Assert.*; +import static org.junit.Assert.fail; import org.junit.Test; @@ -65,5 +65,4 @@ public void testDelete() { public void testNewKeyBuilder() { fail("Not yet implemented"); } - } diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 0184f26717b9..2e44315c715d 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -1,8 +1,11 @@ package com.google.gcloud.datastore; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; -import com.google.common.collect.ImmutableMap; +import com.google.api.services.datastore.DatastoreV1; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; import com.google.gcloud.datastore.Value.Type; import org.junit.Test; @@ -11,7 +14,6 @@ import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.util.Map; public class SerializationTest { @@ -19,53 +21,73 @@ public class SerializationTest { new PartialKey.Builder("ds", "k").addToPath("p", 1).build(); private static final Key KEY1 = new Key.Builder("ds", "k", "n").build(); private static final Key KEY2 = new Key.Builder(INCOMPLETE_KEY, 2).build(); - private static final KeyValue KEY_PROPERTY = new KeyValue(KEY1); - private static final NullValue NULL_PROPERTY = + private static final KeyValue KEY_VALUE = new KeyValue(KEY1); + private static final NullValue NULL_VALUE = new NullValue.Builder().indexed(true).build(); - private static final StringValue STRING_PROPERTY = new StringValue("hello"); + private static final StringValue STRING_VALUE = new StringValue("hello"); + private static final LongValue LONG_VALUE = new LongValue(123); + private static final DoubleValue DOUBLE_VALUE = new DoubleValue(12.34); + private static final BooleanValue BOOLEAN_VALUE = new BooleanValue(true); + private static final DateAndTimeValue DATE_AND_TIME_VALUE = + new DateAndTimeValue(DateAndTime.now()); + private static final BlobValue BLOB_VALUE = + new BlobValue(Blob.copyFrom(new byte[] {10, 0, -2})); + private static final RawValue RAW_VALUE = new RawValue( + DatastoreV1.Value.newBuilder().setBlobKeyValue("blob-key").setMeaning(18).build()); private static final Entity ENTITY1 = new Entity.Builder(KEY1).build(); private static final Entity ENTITY2 = new Entity.Builder(KEY2).setProperty("null", new NullValue()).build(); + private static final Entity ENTITY3 = + new Entity.Builder(KEY2) + .setProperty("p1", new StringValue.Builder("hi1").meaning(10).build()) + .setProperty("p2", new StringValue.Builder("hi2").meaning(11).indexed(false).build()) + .setProperty("p3", new LongValue.Builder(100).indexed(false).meaning(100).build()) + .build(); private static final PartialEntity EMBEDDED_ENTITY1 = ENTITY1; private static final PartialEntity EMBEDDED_ENTITY2 = ENTITY2; private static final PartialEntity EMBEDDED_ENTITY3 = - new PartialEntity.Builder() - .setKey(INCOMPLETE_KEY) - .setProperty("p1", STRING_PROPERTY) - .setProperty("p2", STRING_PROPERTY) + new PartialEntity.Builder(INCOMPLETE_KEY) + .setProperty("p1", STRING_VALUE) + .setProperty("p2", new LongValue.Builder(100).indexed(false).meaning(100).build()) .build(); - private static final PartialEntityValue EMBEDDED_ENTITY_PROPERTY1 = + private static final PartialEntityValue EMBEDDED_ENTITY_VALUE1 = new PartialEntityValue(EMBEDDED_ENTITY1); - private static final PartialEntityValue EMBEDDED_ENTITY_PROPERTY2 = + private static final PartialEntityValue EMBEDDED_ENTITY_VALUE2 = new PartialEntityValue(EMBEDDED_ENTITY2); - private static final PartialEntityValue EMBEDDED_ENTITY_PROPERTY3 = + private static final PartialEntityValue EMBEDDED_ENTITY_VALUE3 = new PartialEntityValue(EMBEDDED_ENTITY3); - private static final ListValue PROPERTY_LIST_PROPERTY = + private static final ListValue LIST_VALUE = new ListValue.Builder() - .addValue(NULL_PROPERTY) - .addValue(STRING_PROPERTY) + .addValue(NULL_VALUE) + .addValue(STRING_VALUE) .addValue(new NullValue()) .build(); - private Map typeToValues = ImmutableMap.of( - Type.NULL, array(NULL_PROPERTY), - Type.KEY, array(KEY_PROPERTY), - Type.STRING, array(STRING_PROPERTY), - Type.PARTIAL_ENTITY, array(EMBEDDED_ENTITY_PROPERTY1, EMBEDDED_ENTITY_PROPERTY2, - EMBEDDED_ENTITY_PROPERTY3), - Type.LIST, array(PROPERTY_LIST_PROPERTY)); - - private static T[] array(T... values) { - return values; - } + @SuppressWarnings("rawtypes") + private Multimap typeToValues = ImmutableMultimap.builder() + .put(Type.NULL, NULL_VALUE) + .put(Type.KEY, KEY_VALUE) + .put(Type.STRING, STRING_VALUE) + .putAll(Type.PARTIAL_ENTITY, EMBEDDED_ENTITY_VALUE1, EMBEDDED_ENTITY_VALUE2, + EMBEDDED_ENTITY_VALUE3) + .put(Type.LIST, LIST_VALUE) + .put(Type.LONG, LONG_VALUE) + .put(Type.DOUBLE, DOUBLE_VALUE) + .put(Type.BOOLEAN, BOOLEAN_VALUE) + .put(Type.DATE_AND_TIME, DATE_AND_TIME_VALUE) + .put(Type.BLOB, BLOB_VALUE) + .put(Type.RAW_VALUE, RAW_VALUE) + .build(); @Test public void testValues() throws Exception { for (Type type : Type.values()) { - Value[] values = typeToValues.get(type); - for (Value value : values) { + for (Value value : typeToValues.get(type)) { Value copy = serialiazeAndDeserialize(value); + assertEquals(value, value); assertEquals(value, copy); + assertNotSame(value, copy); + assertEquals(copy, copy); assertEquals(value.get(), copy.get()); } } @@ -73,25 +95,27 @@ public void testValues() throws Exception { @Test public void testTypes() throws Exception { - Object[] types = { KEY1, KEY2, INCOMPLETE_KEY, ENTITY1, ENTITY2, EMBEDDED_ENTITY1, - EMBEDDED_ENTITY2, EMBEDDED_ENTITY3}; + Object[] types = { KEY1, KEY2, INCOMPLETE_KEY, ENTITY1, ENTITY2, ENTITY3, + EMBEDDED_ENTITY1, EMBEDDED_ENTITY2, EMBEDDED_ENTITY3}; for (Object obj : types) { Object copy = serialiazeAndDeserialize(obj); + assertEquals(obj, obj); assertEquals(obj, copy); + assertNotSame(obj, copy); + assertEquals(copy, copy); } } + @SuppressWarnings("unchecked") private T serialiazeAndDeserialize(T obj) throws Exception { - ByteArrayOutputStream bOut = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(bOut); - out.writeObject(obj); - out.close(); - byte[] bytes = bOut.toByteArray(); - ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); - ObjectInputStream in = new ObjectInputStream(bIn); - @SuppressWarnings("unchecked") - T copy = (T) in.readObject(); - in.close(); - return copy; + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) { + objectOutputStream.writeObject(obj); + } + byte[] bytes = byteArrayOutputStream.toByteArray(); + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + try (ObjectInputStream in = new ObjectInputStream(byteArrayInputStream)) { + return (T) in.readObject(); + } } } From 9baa7214cad1d0f1d6833753944b35e98ba58d29 Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 3 Dec 2014 20:34:01 -0800 Subject: [PATCH 026/771] work in progress --- .../datastore/DatastoreServiceFactory.java | 2 +- .../com/google/gcloud/datastore/Entity.java | 7 ++ .../java/com/google/gcloud/datastore/Key.java | 47 ++++++----- .../google/gcloud/datastore/KeyBuilder.java | 16 +++- .../gcloud/datastore/PartialEntity.java | 10 ++- .../gcloud/datastore/PartialEntityValue.java | 4 +- .../google/gcloud/datastore/PartialKey.java | 55 ++++++++---- .../datastore/DatastoreServiceTest.java | 83 +++++++++++++++++-- .../gcloud/datastore/SerializationTest.java | 14 ++-- 9 files changed, 181 insertions(+), 57 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java index 903e33df5bf9..8d9f88a79ffe 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java @@ -3,7 +3,7 @@ public class DatastoreServiceFactory { - public DatastoreService getDatastoreService(DatastoreServiceOptions options) { + public static DatastoreService getDatastoreService(DatastoreServiceOptions options) { return new DatastoreServiceImpl(options); } } diff --git a/src/main/java/com/google/gcloud/datastore/Entity.java b/src/main/java/com/google/gcloud/datastore/Entity.java index 2bdd2dbfe41b..979174942e06 100644 --- a/src/main/java/com/google/gcloud/datastore/Entity.java +++ b/src/main/java/com/google/gcloud/datastore/Entity.java @@ -28,6 +28,13 @@ public Builder(Entity entity) { super(entity); } + /** + * Create a Builder for the given key and with the properties from the given entity. + */ + public Builder(Key key, PartialEntity entity) { + super(key, entity); + } + @Override public Builder clearProperties() { super.clearProperties(); diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index 33f013e8ad3f..96058259d30c 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -37,40 +37,37 @@ public Builder(String dataset, String kind, long id) { this.id = id; } - public Builder(PartialKey key, String name) { - super(key); + public Builder(Key parent, String kind, String name) { + super(parent, kind); this.name = name; } - public Builder(PartialKey key, long id) { - super(key); + public Builder(Key parent, String kind, long id) { + super(parent, kind); this.id = id; } - public Builder(Key key) { - super(key); - if (key.id() == null) { - name = key.name(); - } else { - id = key.id(); - } + @Override + public Builder addAncestor(String kind, long id) { + super.addAncestor(kind, id); + return this; } @Override - public Builder addToPath(String kind, long id) { - super.addToPath(kind, id); + public Builder addAncestor(String kind, String name) { + super.addAncestor(kind, name); return this; } @Override - public Builder addToPath(String kind, String name) { - super.addToPath(kind, name); + public Builder addAncestor(PathElement... ancestor) { + super.addAncestor(ancestor); return this; } @Override - public Builder addToPath(PathElement pathElement) { - super.addToPath(pathElement); + public Builder addAncestors(Iterable ancestors) { + super.addAncestors(ancestors); return this; } @@ -127,7 +124,13 @@ private Key(PartialKey key, long id) { @Override public Builder builder() { - return new Builder(this); + Builder builder = + hasId() ? new Builder(dataset(), kind(), id()) : new Builder(dataset(), kind(), name()); + return builder.namespace(namespace()).addAncestors(ancestors()); + } + + public boolean hasId() { + return id() != null; } /** @@ -137,6 +140,10 @@ public Long id() { return getLeaf().id(); } + public boolean hasName() { + return name() != null; + } + /** * Returns the key's name or {@code null} if it has an id instead. */ @@ -210,14 +217,14 @@ static Key fromPb(DatastoreV1.Key keyPb) { private static ImmutableList newPath(PartialKey key, String name) { ImmutableList.Builder path = ImmutableList.builder(); - path.addAll(key.ancestorPath()); + path.addAll(key.ancestors()); path.add(new PathElement(key.kind(), name)); return path.build(); } private static ImmutableList newPath(PartialKey key, long id) { ImmutableList.Builder path = ImmutableList.builder(); - path.addAll(key.ancestorPath()); + path.addAll(key.ancestors()); path.add(new PathElement(key.kind(), id)); return path.build(); } diff --git a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java index 07c6382f0dc5..39c8653a6d64 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java +++ b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java @@ -20,12 +20,12 @@ public KeyBuilder(DatastoreService service, String kind) { } public KeyBuilder addToPath(String kind, String name) { - delegate.addToPath(kind, name); + delegate.addAncestor(kind, name); return this; } public KeyBuilder addToPath(String kind, long id) { - delegate.addToPath(kind, id); + delegate.addAncestor(kind, id); return this; } @@ -43,11 +43,19 @@ public Key allocateIdAndBuild() { } public Key build(String name) { - return new Key.Builder(build(), name).build(); + PartialKey key = build(); + return new Key.Builder(key.dataset(), key.kind(), name) + .namespace(key.namespace()) + .addAncestors(key.ancestors()) + .build(); } public Key build(long id) { - return new Key.Builder(build(), id).build(); + PartialKey key = build(); + return new Key.Builder(key.dataset(), key.kind(), id) + .namespace(key.namespace()) + .addAncestors(key.ancestors()) + .build(); } public PartialKey build() { diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java index c13ea5679b64..7d22be430372 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java @@ -41,6 +41,11 @@ public Builder(PartialEntity entity) { properties = new HashMap<>(entity.properties()); } + public Builder(PartialKey key, PartialEntity entity) { + this.key = key; + properties = new HashMap<>(entity.properties()); + } + public Builder clearProperties() { properties.clear(); return this; @@ -78,9 +83,8 @@ public boolean hasProperty(String name) { } @SuppressWarnings("unchecked") - public , B extends Value.Builder> Value property( - String name) { - return (Value) properties.get(name); + public > V property(String name) { + return (V) properties.get(name); } public Set propertyNames() { diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java b/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java index cc4dd834a343..db076a3305a0 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java @@ -5,7 +5,7 @@ import com.google.api.services.datastore.DatastoreV1; public class PartialEntityValue extends -Value { + Value { private static final long serialVersionUID = -5461475706792576395L; @@ -34,7 +34,7 @@ protected void setValue(PartialEntityValue from, DatastoreV1.Value.Builder to) { }; public static final class Builder extends - Value.BaseBuilder { + Value.BaseBuilder { public Builder(PartialEntity entity) { super(Type.PARTIAL_ENTITY); diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index 4bcca81ddb64..7827b66dcd33 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -135,27 +135,38 @@ public Builder(String dataset, String kind) { this.kind = validateKind(kind); } - public Builder(PartialKey key) { - dataset = key.dataset; - namespace = key.namespace; - kind = key.kind(); - path.addAll(key.ancestorPath()); + public Builder(Key parent, String kind) { + dataset = parent.dataset(); + namespace = parent.namespace(); + path.addAll(parent.ancestors()); + path.add(parent.getLeaf()); + this.kind = kind; } - public Builder addToPath(String kind, long id) { + public Builder addAncestor(String kind, long id) { checkArgument(id != 0, "id must not be equal to zero"); - return addToPath(new PathElement(kind, id)); + return addAncestor(new PathElement(kind, id)); } - public Builder addToPath(String kind, String name) { + public Builder addAncestor(String kind, String name) { checkArgument(Strings.isNullOrEmpty(name) , "name must not be empty or null"); checkArgument(name.length() <= 500, "name must not exceed 500 characters"); - return addToPath(new PathElement(kind, name)); + return addAncestor(new PathElement(kind, name)); + } + + public Builder addAncestor(PathElement... ancestor) { + Preconditions.checkState(path.size() + ancestor.length <= MAX_PATH, + "path can have at most 100 elements"); + for (PathElement pathElement : ancestor) { + path.add(pathElement); + } + return this; } - public Builder addToPath(PathElement pathElement) { - Preconditions.checkState(path.size() < MAX_PATH, "path can have at most 100 elements"); - path.add(pathElement); + public Builder addAncestors(Iterable ancestors) { + for (PathElement pathElement : ancestors) { + addAncestor(pathElement); + } return this; } @@ -215,9 +226,9 @@ public String namespace() { } /** - * Returns the key's parent's path. + * Returns an immutable list with the key's ancestors. */ - public List ancestorPath() { + public List ancestors() { return path.subList(0, path.size() - 1); } @@ -229,7 +240,21 @@ public String kind() { } public Builder builder() { - return new Builder(this); + return new Builder(dataset(), kind()).namespace(namespace()).addAncestors(ancestors()); + } + + public Key toKey(String name) { + return new Key.Builder(dataset(), kind(), name) + .namespace(namespace()) + .addAncestors(ancestors()) + .build(); + } + + public Key toKey(long id) { + return new Key.Builder(dataset(), kind(), id) + .namespace(namespace()) + .addAncestors(ancestors()) + .build(); } @Override diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index ab8e839c15b2..e7944009eebb 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -1,14 +1,52 @@ package com.google.gcloud.datastore; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import org.junit.Before; import org.junit.Test; +import java.util.Iterator; + public class DatastoreServiceTest { + private static final String DATASET = "dataset1"; + private static final String KIND1 = "kind1"; + private static final String KIND2 = "kind2"; + private static final NullValue NULL_VALUE = new NullValue(); + private static final StringValue STR_VALUE = new StringValue("str"); + private static final BooleanValue BOOL_VALUE = + new BooleanValue.Builder(false).indexed(false).build(); + private static final PartialKey PARTIAL_KEY1 = new PartialKey.Builder(DATASET, KIND1).build(); + private static final PartialKey PARTIAL_KEY2 = new PartialKey.Builder(DATASET, KIND2).build(); + private static final Key KEY1 = PARTIAL_KEY1.toKey("name"); + private static final Key KEY2 = new Key.Builder(KEY1, KIND2, 1).build(); + private static final Key KEY3 = KEY2.builder().name("bla").build(); + private static final Entity ENTITY1 = new Entity.Builder(KEY1) + .setProperty("str", STR_VALUE) + .setProperty("bool", BOOL_VALUE) + .build(); + private static final Entity ENTITY2 = new Entity.Builder(KEY2, ENTITY1) + .removeProperty("str") + .setProperty("null", NULL_VALUE) + .build(); + + private DatastoreServiceOptions options; + private DatastoreService datastore; + + @Before + public void setUp() { + options = new DatastoreServiceOptions.Builder().dataset(DATASET).build(); + datastore = DatastoreServiceFactory.getDatastoreService(options); + } + @Test public void testGetOptions() { - fail("Not yet implemented"); + assertSame(options, datastore.getOptions()); } @Test @@ -32,13 +70,41 @@ public void testAllocateIds() { } @Test - public void testGetKey() { - fail("Not yet implemented"); + public void testAddAndGet() { + Entity entity = datastore.get(KEY1); + assertNull(entity); + + datastore.add(ENTITY1); + + entity = datastore.get(KEY1); + StringValue value1 = entity.property("str"); + BooleanValue value2 = entity.property("bool"); + assertEquals(value1, STR_VALUE); + assertEquals(value2, BOOL_VALUE); + assertEquals(2, entity.propertyNames().size()); + assertTrue(entity.propertyNames().contains("str")); + assertTrue(entity.propertyNames().contains("bool")); + assertFalse(entity.hasProperty("bla")); } @Test - public void testGetKeyArray() { - fail("Not yet implemented"); + public void testAddAndGetArray() { + Iterator result = datastore.get(KEY1, KEY2); + assertNull(result.next()); + assertNull(result.next()); + assertFalse(result.hasNext()); + + populateDatastore(); + + result = datastore.get(KEY1, KEY1.builder().name("bla").build(), KEY2); + assertEquals(ENTITY1, result.next()); + assertNull(result.next()); + assertEquals(ENTITY2, result.next()); + assertFalse(result.hasNext()); + } + + private void populateDatastore() { + datastore.add(ENTITY1, ENTITY2); } @Test @@ -63,6 +129,11 @@ public void testDelete() { @Test public void testNewKeyBuilder() { - fail("Not yet implemented"); + KeyBuilder keyBuilder = datastore.newKeyBuilder(KIND1); + assertEquals(PARTIAL_KEY1, keyBuilder.build()); + assertEquals(PARTIAL_KEY1.builder().kind(KIND2).build(), + datastore.newKeyBuilder(KIND2).build()); + assertEquals(KEY1, keyBuilder.build("name")); + assertEquals(KEY1.builder().id(2).build(), keyBuilder.build(2)); } } diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 2e44315c715d..d450d84f08b8 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -17,10 +17,12 @@ public class SerializationTest { - private static final PartialKey INCOMPLETE_KEY = - new PartialKey.Builder("ds", "k").addToPath("p", 1).build(); + private static final PartialKey INCOMPLETE_KEY1 = + new PartialKey.Builder("ds", "k").addAncestor("p", 1).build(); private static final Key KEY1 = new Key.Builder("ds", "k", "n").build(); - private static final Key KEY2 = new Key.Builder(INCOMPLETE_KEY, 2).build(); + private static final PartialKey INCOMPLETE_KEY2 = + new PartialKey.Builder(KEY1, "v").addAncestor("p", 1).build(); + private static final Key KEY2 = new Key.Builder(KEY1, "v", 2).build(); private static final KeyValue KEY_VALUE = new KeyValue(KEY1); private static final NullValue NULL_VALUE = new NullValue.Builder().indexed(true).build(); @@ -46,7 +48,7 @@ public class SerializationTest { private static final PartialEntity EMBEDDED_ENTITY1 = ENTITY1; private static final PartialEntity EMBEDDED_ENTITY2 = ENTITY2; private static final PartialEntity EMBEDDED_ENTITY3 = - new PartialEntity.Builder(INCOMPLETE_KEY) + new PartialEntity.Builder(INCOMPLETE_KEY1) .setProperty("p1", STRING_VALUE) .setProperty("p2", new LongValue.Builder(100).indexed(false).meaning(100).build()) .build(); @@ -95,8 +97,8 @@ public void testValues() throws Exception { @Test public void testTypes() throws Exception { - Object[] types = { KEY1, KEY2, INCOMPLETE_KEY, ENTITY1, ENTITY2, ENTITY3, - EMBEDDED_ENTITY1, EMBEDDED_ENTITY2, EMBEDDED_ENTITY3}; + Object[] types = { KEY1, KEY2, INCOMPLETE_KEY1, INCOMPLETE_KEY2, ENTITY1, ENTITY2, + ENTITY3, EMBEDDED_ENTITY1, EMBEDDED_ENTITY2, EMBEDDED_ENTITY3}; for (Object obj : types) { Object copy = serialiazeAndDeserialize(obj); assertEquals(obj, obj); From 8c1f432360305bc012cb4b067929479f2b9dc12f Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 3 Dec 2014 23:01:41 -0800 Subject: [PATCH 027/771] work in progress --- .../java/com/google/gcloud/AuthConfig.java | 8 +- .../com/google/gcloud/ServiceOptions.java | 27 +++-- .../datastore/DatastoreServiceException.java | 71 ++++++++++-- .../datastore/DatastoreServiceFactory.java | 34 +++++- .../datastore/DatastoreServiceImpl.java | 106 ++++++++++++++++-- .../datastore/DatastoreServiceOptions.java | 19 +++- .../google/gcloud/datastore/KeyBuilder.java | 4 +- .../datastore/DatastoreServiceTest.java | 21 +++- 8 files changed, 244 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/google/gcloud/AuthConfig.java b/src/main/java/com/google/gcloud/AuthConfig.java index d6321e5488d7..a6e47536e9e7 100644 --- a/src/main/java/com/google/gcloud/AuthConfig.java +++ b/src/main/java/com/google/gcloud/AuthConfig.java @@ -19,7 +19,7 @@ public abstract class AuthConfig { private static class AppEngineAuthConfig extends AuthConfig { @Override - protected HttpRequestInitializer getHttpRequestInitializer( + protected HttpRequestInitializer httpRequestInitializer( HttpTransport transport, Set scopes) { return new AppIdentityCredential(scopes); } @@ -36,7 +36,7 @@ public ServiceAccountAuthConfig(String account, PrivateKey privateKey) { } @Override - protected HttpRequestInitializer getHttpRequestInitializer( + protected HttpRequestInitializer httpRequestInitializer( HttpTransport transport, Set scopes) { return new GoogleCredential.Builder() .setTransport(transport) @@ -48,7 +48,7 @@ protected HttpRequestInitializer getHttpRequestInitializer( } } - protected abstract HttpRequestInitializer getHttpRequestInitializer( + protected abstract HttpRequestInitializer httpRequestInitializer( HttpTransport transport, Set scopes); @@ -60,7 +60,7 @@ public static AuthConfig createForComputeEngine() throws IOException, GeneralSec final ComputeCredential cred = getComputeCredential(); return new AuthConfig() { @Override - protected HttpRequestInitializer getHttpRequestInitializer(HttpTransport ts, Set sc) { + protected HttpRequestInitializer httpRequestInitializer(HttpTransport ts, Set sc) { return cred; } }; diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java index 137b9856bf0d..ff001b06fa6b 100644 --- a/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/src/main/java/com/google/gcloud/ServiceOptions.java @@ -4,6 +4,7 @@ import static com.google.common.base.MoreObjects.firstNonNull; import com.google.api.client.extensions.appengine.http.UrlFetchTransport; +import com.google.api.client.http.HttpRequestInitializer; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; @@ -63,7 +64,7 @@ protected static String getAppEngineAppId() { return System.getProperty("com.google.appengine.application.id"); } - protected abstract static class Builder { + protected abstract static class Builder> { private String host; private HttpTransport httpTransport; @@ -79,33 +80,37 @@ protected Builder(ServiceOptions options) { protected abstract ServiceOptions build(); - public Builder host(String host) { + public B host(String host) { this.host = host; - return this; + return (B) this; } - public Builder httpTransport(HttpTransport httpTransport) { + public B httpTransport(HttpTransport httpTransport) { this.httpTransport = httpTransport; - return this; + return (B) this; } - public Builder authConfig(AuthConfig authConfig) { + public B authConfig(AuthConfig authConfig) { this.authConfig = authConfig; - return this; + return (B) this; } } - protected abstract Set getScopes(); + protected abstract Set scopes(); - public String getHost() { + public String host() { return host; } - public HttpTransport getHttpTransport() { + public HttpTransport httpTransport() { return httpTransport; } - public AuthConfig getAuthConfig() { + public AuthConfig authConfig() { return authConfig; } + + protected HttpRequestInitializer httpRequestInitializer() { + return authConfig().httpRequestInitializer(httpTransport, scopes()); + } } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java index 5b27cd519654..912efe4a4692 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java @@ -1,20 +1,77 @@ package com.google.gcloud.datastore; +import static com.google.common.base.MoreObjects.firstNonNull; + +import com.google.api.services.datastore.client.DatastoreException; + +import java.util.HashMap; +import java.util.Map; + public class DatastoreServiceException extends RuntimeException { private static final long serialVersionUID = 8170357898917041899L; + private static final Map HTTP_TO_CODE = new HashMap<>(); + + private final Code code; + + /** + * An error code to represent the failure. + * + * @see Google Cloud Datastore error codes + */ + public enum Code { + + ABORTED(409, true, "Request aborted"), + DEADLINE_EXCEEDED(403, true, "Deadline exceeded"), + UNAVAILABLE(503, true, "Could not reach service"), + FAILED_PRECONDITION(412, false, "Invalid request"), + INVALID_ARGUMENT(400, false, "Request parameter has an invalid value"), + PERMISSION_DENIED(403, false, "Unauthorized request"), + RESOURCE_EXHAUSTED(402, false, "Quota exceeded"), + INTERNAL(500, false, "Server returned an error"), + UNKNOWN(0, false, "Unknown failure"); - private final boolean isTransient; + private final boolean isTransient; + private final String msg; - public DatastoreServiceException(boolean isTransient, String msg, Exception cause) { - super(msg, cause); - this.isTransient = isTransient; + Code(int httpStatus, boolean isTransient, String msg) { + this.isTransient = isTransient; + this.msg = msg; + HTTP_TO_CODE.put(httpStatus, this); + } + + /** + * Returns {@code true} if this exception is transient and the same request could be retried. + * For any retry it is highly recommended to apply an exponential backoff. + */ + public boolean isTransient() { + return isTransient; + } + + DatastoreServiceException translate(DatastoreException exception) { + return new DatastoreServiceException(this, exception); + } + } + + public DatastoreServiceException(Code code, Exception cause) { + super(code.msg, cause); + this.code = code; + } + + /** + * Returns the code associated with this exception. + */ + public Code code() { + return code; } /** - * @return {@code true} if this exception is transient and could be safely retried. + * Translate DatastoreException to DatastoreServiceException based on their + * HTTP error codes. This method will always throw a new DatastoreServiceException. + * + * @throws DatastoreServiceException for every given DatastoreException */ - public boolean isTransient() { - return isTransient; + static DatastoreServiceException translateAndPropagate(DatastoreException exception) { + throw firstNonNull(HTTP_TO_CODE.get(exception.getCode()), Code.UNKNOWN).translate(exception); } } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java index 8d9f88a79ffe..4e69efe743d1 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java @@ -1,9 +1,37 @@ package com.google.gcloud.datastore; +import com.google.api.services.datastore.client.Datastore; +import com.google.api.services.datastore.client.DatastoreFactory; +import com.google.api.services.datastore.client.DatastoreOptions; +import com.google.api.services.datastore.client.LocalDevelopmentDatastoreFactory; -public class DatastoreServiceFactory { - public static DatastoreService getDatastoreService(DatastoreServiceOptions options) { - return new DatastoreServiceImpl(options); +public interface DatastoreServiceFactory { + + enum Mode implements DatastoreServiceFactory { + + TESTING { + + @Override + public DatastoreService get(DatastoreServiceOptions options) { + DatastoreOptions dsOptions = new DatastoreOptions.Builder() + .dataset(options.dataset()) + .host(options.host()) + .build(); + Datastore datastore = LocalDevelopmentDatastoreFactory.get().create(dsOptions); + return new DatastoreServiceImpl(options, datastore); + } + }, + + PROD { + @Override + public DatastoreService get(DatastoreServiceOptions options) { + DatastoreOptions dsOptions = options.toDatastoreOptions(); + Datastore datastore = DatastoreFactory.get().create(dsOptions); + return new DatastoreServiceImpl(options, datastore); + } + }; } + + DatastoreService get(DatastoreServiceOptions options); } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index fb73952613dc..499a1893b243 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -1,14 +1,23 @@ package com.google.gcloud.datastore; +import com.google.api.services.datastore.DatastoreV1; +import com.google.api.services.datastore.client.Datastore; +import com.google.api.services.datastore.client.DatastoreException; +import com.google.common.collect.AbstractIterator; + +import java.util.HashMap; import java.util.Iterator; +import java.util.Map; final class DatastoreServiceImpl implements DatastoreService { private final DatastoreServiceOptions options; + private final Datastore datastore; - DatastoreServiceImpl(DatastoreServiceOptions options) { + DatastoreServiceImpl(DatastoreServiceOptions options, Datastore datastore) { this.options = options; + this.datastore = datastore; } @Override @@ -35,9 +44,27 @@ public Key allocateId(PartialKey key) { @Override public Iterator allocateIds(PartialKey... key) { - // TODO Auto-generated method stub - // Will need to populate "force" after b/18594027 is fixed. - return null; + DatastoreV1.AllocateIdsRequest.Builder requestPb = DatastoreV1.AllocateIdsRequest.newBuilder(); + for (PartialKey k : key) { + requestPb.addKey(k.toPb()); + } + // TODO(ozarov): will need to populate "force" after b/18594027 is fixed. + try { + DatastoreV1.AllocateIdsResponse responsePb = datastore.allocateIds(requestPb.build()); + final Iterator keys = responsePb.getKeyList().iterator(); + return new AbstractIterator() { + + @Override + protected Key computeNext() { + if (keys.hasNext()) { + return Key.fromPb(keys.next()); + } + return endOfData(); + } + }; + } catch (DatastoreException e) { + throw DatastoreServiceException.translateAndPropagate(e); + } } @Override @@ -46,29 +73,82 @@ public Entity get(Key key) { } @Override - public Iterator get(Key... key) { - // TODO Auto-generated method stub - return null; + public Iterator get(final Key... key) { + DatastoreV1.LookupRequest.Builder requestPb = DatastoreV1.LookupRequest.newBuilder(); + for (Key k : key) { + requestPb.addKey(k.toPb()); + } + try { + DatastoreV1.LookupResponse responsePb = datastore.lookup(requestPb.build()); + final Map result = new HashMap<>(); + for (DatastoreV1.EntityResult entityResultPb : responsePb.getFoundList()) { + Entity entity = Entity.fromPb(entityResultPb.getEntity()); + result.put(entity.key(), entity); + } + return new AbstractIterator() { + int index; + + @Override + protected Entity computeNext() { + if (index < key.length) { + return result.get(key[index++]); + } + return endOfData(); + } + }; + } catch (DatastoreException e) { + throw DatastoreServiceException.translateAndPropagate(e); + } } @Override public void add(Entity... entity) { - // TODO Auto-generated method stub + DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); + for (Entity e : entity) { + mutationPb.addInsert(e.toPb()); + } + comitMutation(mutationPb); + } + + private void comitMutation(DatastoreV1.Mutation.Builder mutationPb) { + if (options.force()) { + mutationPb.setForce(true); + } + DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder(); + requestPb.setMode(DatastoreV1.CommitRequest.Mode.NON_TRANSACTIONAL); + requestPb.setMutation(mutationPb.build()); + try { + datastore.commit(requestPb.build()); + } catch (DatastoreException e) { + throw DatastoreServiceException.translateAndPropagate(e); + } } @Override public void update(Entity... entity) { - // TODO Auto-generated method stub + DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); + for (Entity e : entity) { + mutationPb.addUpdate(e.toPb()); + } + comitMutation(mutationPb); } @Override public void put(Entity... entity) { - // TODO Auto-generated method stub + DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); + for (Entity e : entity) { + mutationPb.addUpsert(e.toPb()); + } + comitMutation(mutationPb); } @Override public void delete(Key... key) { - // TODO Auto-generated method stub + DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); + for (Key k : key) { + mutationPb.addDelete(k.toPb()); + } + comitMutation(mutationPb); } @Override @@ -81,4 +161,8 @@ public QueryResult runQuery(Query query) { // TODO Auto-generated method stub return null; } + + Datastore datastore() { + return datastore; + } } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java index 06377531b25a..74d218663c76 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java @@ -3,6 +3,7 @@ import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkArgument; +import com.google.api.services.datastore.client.DatastoreOptions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import com.google.gcloud.ServiceOptions; @@ -29,7 +30,7 @@ public class DatastoreServiceOptions extends ServiceOptions { force = builder.force; } - public static class Builder extends ServiceOptions.Builder { + public static class Builder extends ServiceOptions.Builder { private String dataset; private boolean force = false; @@ -69,11 +70,11 @@ static String validateDataset(String dataset) { return dataset; } - public String getDataset() { + public String dataset() { return dataset; } - public String getDefaultNamespace() { + public String defaultNamespace() { // TODO(ozarov): An alternative to reflection would be to depend on AE api jar: // http://mvnrepository.com/artifact/com.google.appengine/appengine-api-1.0-sdk/1.2.0 try { @@ -85,12 +86,20 @@ public String getDefaultNamespace() { } } - public boolean getForce() { + public boolean force() { return force; } @Override - protected Set getScopes() { + protected Set scopes() { return SCOPES; } + + DatastoreOptions toDatastoreOptions() { + return new DatastoreOptions.Builder() + .dataset(dataset()) + .host(host()) + .initializer(httpRequestInitializer()) + .build(); + } } diff --git a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java index 39c8653a6d64..11b6af1f909c 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java +++ b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java @@ -15,8 +15,8 @@ public final class KeyBuilder { */ public KeyBuilder(DatastoreService service, String kind) { this.service = checkNotNull(service); - delegate = new PartialKey.Builder(service.getOptions().getDataset(), kind); - delegate.namespace(service.getOptions().getDefaultNamespace()); + delegate = new PartialKey.Builder(service.getOptions().dataset(), kind); + delegate.namespace(service.getOptions().defaultNamespace()); } public KeyBuilder addToPath(String kind, String name) { diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index e7944009eebb..fb00bb4dfa44 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -7,6 +7,10 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.api.services.datastore.client.LocalDevelopmentDatastore; +import com.google.api.services.datastore.client.LocalDevelopmentDatastoreException; + +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -37,11 +41,22 @@ public class DatastoreServiceTest { private DatastoreServiceOptions options; private DatastoreService datastore; + private LocalDevelopmentDatastore localDatastore; @Before - public void setUp() { - options = new DatastoreServiceOptions.Builder().dataset(DATASET).build(); - datastore = DatastoreServiceFactory.getDatastoreService(options); + public void setUp() throws LocalDevelopmentDatastoreException { + options = new DatastoreServiceOptions.Builder() + .dataset(DATASET) + .host("http://localhost:8080/") + .build(); + datastore = DatastoreServiceFactory.Mode.TESTING.get(options); + localDatastore = (LocalDevelopmentDatastore) ((DatastoreServiceImpl) datastore).datastore(); + localDatastore.start("/usr/local/gcd-sdk", DATASET); + } + + @After + public void tearDown() throws LocalDevelopmentDatastoreException { + localDatastore.stop(); } @Test From 364f00abba08f6b13acd116f95d8bc2bac38b017 Mon Sep 17 00:00:00 2001 From: aozarov Date: Thu, 4 Dec 2014 16:26:04 -0800 Subject: [PATCH 028/771] Add more tests and package-info --- .../java/com/google/gcloud/AuthConfig.java | 20 ++- .../com/google/gcloud/ServiceOptions.java | 19 ++- .../gcloud/datastore/DatastoreService.java | 5 +- .../datastore/DatastoreServiceFactory.java | 25 +-- .../datastore/DatastoreServiceImpl.java | 7 + .../datastore/DatastoreServiceOptions.java | 51 ++++-- .../java/com/google/gcloud/datastore/Key.java | 20 +-- .../google/gcloud/datastore/KeyBuilder.java | 57 ++++--- .../google/gcloud/datastore/NullValue.java | 2 +- .../google/gcloud/datastore/PartialKey.java | 103 ++++++------ .../google/gcloud/datastore/package-info.java | 31 ++++ .../datastore/DatastoreServiceTest.java | 154 +++++++++++++----- 12 files changed, 326 insertions(+), 168 deletions(-) create mode 100644 src/main/java/com/google/gcloud/datastore/package-info.java diff --git a/src/main/java/com/google/gcloud/AuthConfig.java b/src/main/java/com/google/gcloud/AuthConfig.java index a6e47536e9e7..117db5434a00 100644 --- a/src/main/java/com/google/gcloud/AuthConfig.java +++ b/src/main/java/com/google/gcloud/AuthConfig.java @@ -8,6 +8,7 @@ import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson.JacksonFactory; +import com.google.api.client.repackaged.com.google.common.base.Preconditions; import java.io.IOException; import java.security.GeneralSecurityException; @@ -33,18 +34,23 @@ private static class ServiceAccountAuthConfig extends AuthConfig { public ServiceAccountAuthConfig(String account, PrivateKey privateKey) { this.account = account; this.privateKey = privateKey; + if (privateKey != null) { + Preconditions.checkArgument(account != null); + } } @Override protected HttpRequestInitializer httpRequestInitializer( HttpTransport transport, Set scopes) { - return new GoogleCredential.Builder() - .setTransport(transport) - .setJsonFactory(new JacksonFactory()) - .setServiceAccountId(account) - .setServiceAccountScopes(scopes) - .setServiceAccountPrivateKey(privateKey) - .build(); + GoogleCredential.Builder builder = new GoogleCredential.Builder() + .setTransport(transport) + .setJsonFactory(new JacksonFactory()) + .setServiceAccountId(account) + .setServiceAccountPrivateKey(privateKey); + if (privateKey != null) { + builder.setServiceAccountScopes(scopes); + } + return builder.build(); } } diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java index ff001b06fa6b..908710ceb750 100644 --- a/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/src/main/java/com/google/gcloud/ServiceOptions.java @@ -18,15 +18,15 @@ public abstract class ServiceOptions { private final HttpTransport httpTransport; private final AuthConfig authConfig; - protected ServiceOptions(Builder builder) { + protected ServiceOptions(Builder builder) { host = firstNonNull(builder.host, DEFAULT_HOST); - httpTransport = firstNonNull(builder.httpTransport, getDefaultHttpTransport()); - authConfig = firstNonNull(builder.authConfig, getDefaultAuthConfig()); + httpTransport = firstNonNull(builder.httpTransport, defaultHttpTransport()); + authConfig = firstNonNull(builder.authConfig, defaultAuthConfig()); } - private static HttpTransport getDefaultHttpTransport() { + private static HttpTransport defaultHttpTransport() { // Consider App Engine - if (getAppEngineAppId() != null) { + if (appEngineAppId() != null) { try { return new UrlFetchTransport(); } catch (Exception ignore) { @@ -42,9 +42,9 @@ private static HttpTransport getDefaultHttpTransport() { return new NetHttpTransport(); } - private static AuthConfig getDefaultAuthConfig() { + private static AuthConfig defaultAuthConfig() { // Consider App Engine - if (getAppEngineAppId() != null) { + if (appEngineAppId() != null) { try { return AuthConfig.createForAppEngine(); } catch (Exception ignore) { @@ -60,7 +60,7 @@ private static AuthConfig getDefaultAuthConfig() { return AuthConfig.createForAccount(null, null); } - protected static String getAppEngineAppId() { + protected static String appEngineAppId() { return System.getProperty("com.google.appengine.application.id"); } @@ -80,16 +80,19 @@ protected Builder(ServiceOptions options) { protected abstract ServiceOptions build(); + @SuppressWarnings("unchecked") public B host(String host) { this.host = host; return (B) this; } + @SuppressWarnings("unchecked") public B httpTransport(HttpTransport httpTransport) { this.httpTransport = httpTransport; return (B) this; } + @SuppressWarnings("unchecked") public B authConfig(AuthConfig authConfig) { this.authConfig = authConfig; return (B) this; diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index 1b67fbe447eb..37fb52ad701f 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -32,7 +32,9 @@ public interface DatastoreService extends DatastoreReader, DatastoreWriter { BatchWriter newBatchWriter(BatchWriteOption... batchWriteOption); /** - * Returns a key with a newly allocated id. + * Allocate a unique id for the given key. + * The returned key will have the same information (dataset, kind, namespace and ancestors) + * as the given key and will have a newly assigned id. * * @throws DatastoreServiceExcepiton upon failure */ @@ -41,6 +43,7 @@ public interface DatastoreService extends DatastoreReader, DatastoreWriter { /** * Returns a list of keys using the allocated ids ordered by the input. * + * @see #allocateId(PartialKey) * @throws DatastoreServiceExcepiton upon failure */ Iterator allocateIds(PartialKey... key); diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java index 4e69efe743d1..2ae4a3011836 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java @@ -3,27 +3,11 @@ import com.google.api.services.datastore.client.Datastore; import com.google.api.services.datastore.client.DatastoreFactory; import com.google.api.services.datastore.client.DatastoreOptions; -import com.google.api.services.datastore.client.LocalDevelopmentDatastoreFactory; -public interface DatastoreServiceFactory { +public abstract class DatastoreServiceFactory { - enum Mode implements DatastoreServiceFactory { - - TESTING { - - @Override - public DatastoreService get(DatastoreServiceOptions options) { - DatastoreOptions dsOptions = new DatastoreOptions.Builder() - .dataset(options.dataset()) - .host(options.host()) - .build(); - Datastore datastore = LocalDevelopmentDatastoreFactory.get().create(dsOptions); - return new DatastoreServiceImpl(options, datastore); - } - }, - - PROD { + private static final DatastoreServiceFactory INSTANCE = new DatastoreServiceFactory() { @Override public DatastoreService get(DatastoreServiceOptions options) { DatastoreOptions dsOptions = options.toDatastoreOptions(); @@ -31,7 +15,10 @@ public DatastoreService get(DatastoreServiceOptions options) { return new DatastoreServiceImpl(options, datastore); } }; + + public static DatastoreService getDefault(DatastoreServiceOptions options) { + return INSTANCE.get(options); } - DatastoreService get(DatastoreServiceOptions options); + public abstract DatastoreService get(DatastoreServiceOptions options); } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index 499a1893b243..4dcae9612568 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -46,6 +46,13 @@ public Key allocateId(PartialKey key) { public Iterator allocateIds(PartialKey... key) { DatastoreV1.AllocateIdsRequest.Builder requestPb = DatastoreV1.AllocateIdsRequest.newBuilder(); for (PartialKey k : key) { + if (k.getLeaf().nameOrId() != null) { + // if key is full remove the id or name part + k = new PartialKey.Builder(k.dataset(), k.kind()) + .namespace(k.namespace()) + .addAncestors(k.ancestors()) + .build(); + } requestPb.addKey(k.toPb()); } // TODO(ozarov): will need to populate "force" after b/18594027 is fixed. diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java index 74d218663c76..8b2f906283d9 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java @@ -17,22 +17,28 @@ public class DatastoreServiceOptions extends ServiceOptions { private static final String DATASTORE_SCOPE = "https://www.googleapis.com/auth/datastore"; private static final String USERINFO_SCOPE = "https://www.googleapis.com/auth/userinfo.email"; private static final Set SCOPES = ImmutableSet.of(DATASTORE_SCOPE, USERINFO_SCOPE); - private static final Pattern PATTERN = Pattern.compile( + private static final Pattern DATASET_PATTERN = Pattern.compile( "([a-z\\d\\-]{1,100}~)?([a-z\\d][a-z\\d\\-\\.]{0,99}\\:)?([a-z\\d][a-z\\d\\-]{0,99})"); + private static final int MAX_NAMESPACE_LENGTH = 100; + private static final Pattern NAMESPACE_PATTERN = + Pattern.compile(String.format("[0-9A-Za-z\\._\\-]{0,%d}", MAX_NAMESPACE_LENGTH)); private final String dataset; + private final String namespace; private final boolean force; DatastoreServiceOptions(Builder builder) { super(builder); - dataset = firstNonNull(builder.dataset, getAppEngineAppId()); + dataset = firstNonNull(builder.dataset, appEngineAppId()); checkArgument(dataset != null, "missing dataset"); + namespace = builder.namespace != null ? builder.namespace : defaultNamespace(); force = builder.force; } public static class Builder extends ServiceOptions.Builder { private String dataset; + private String namespace; private boolean force = false; public Builder() {} @@ -53,36 +59,53 @@ public Builder dataset(String dataset) { return this; } + public Builder namespace(String namespace) { + this.namespace = validateNamespace(namespace); + return this; + } + public Builder force(boolean force) { this.force = force; return this; } } - static String validateDataset(String dataset) { - if (Strings.isNullOrEmpty(dataset)) { - throw new IllegalArgumentException("dataset can't be empty or null"); - } - if (!PATTERN.matcher(dataset).matches()) { - throw new IllegalArgumentException( - "dataset must match the following pattern: " + PATTERN.pattern()); - } + public String dataset() { return dataset; } - public String dataset() { + public String namespace() { + return namespace; + } + + static String validateDataset(String dataset) { + checkArgument(!Strings.isNullOrEmpty(dataset), "dataset can't be empty or null"); + checkArgument(DATASET_PATTERN.matcher(dataset).matches(), + "dataset must match the following pattern: " + DATASET_PATTERN.pattern()); return dataset; } - public String defaultNamespace() { + static String validateNamespace(String namespace) { + if (namespace != null) { + checkArgument(!namespace.isEmpty(), "namespace must not be an empty string"); + checkArgument(namespace.length() <= 100, + "namespace must not contain more than 100 characters"); + checkArgument(NAMESPACE_PATTERN.matcher(namespace).matches(), + "namespace must the following pattern: " + NAMESPACE_PATTERN.pattern()); + } + return namespace; + } + + private static String defaultNamespace() { // TODO(ozarov): An alternative to reflection would be to depend on AE api jar: // http://mvnrepository.com/artifact/com.google.appengine/appengine-api-1.0-sdk/1.2.0 try { Class clazz = Class.forName("com.google.appengine.api.NamespaceManager"); Method method = clazz.getMethod("get"); - return (String) method.invoke(null); + String namespace = (String) method.invoke(null); + return "".equals(namespace) ? null : namespace; } catch (Exception ex) { - return ""; + return null; } } diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index 96058259d30c..c490dded05f5 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -60,13 +60,13 @@ public Builder addAncestor(String kind, String name) { } @Override - public Builder addAncestor(PathElement... ancestor) { + public Builder addAncestor(Ancestor... ancestor) { super.addAncestor(ancestor); return this; } @Override - public Builder addAncestors(Iterable ancestors) { + public Builder addAncestors(Iterable ancestors) { super.addAncestors(ancestors); return this; } @@ -155,7 +155,7 @@ public String name() { * Returns the key's id (as {@link #Long}) or name (as {@link String}). */ public Object nameOrId() { - PathElement leaf = getLeaf(); + Ancestor leaf = getLeaf(); return leaf.hasId() ? leaf.id() : leaf.name(); } @@ -197,7 +197,7 @@ public static Key fromIncompleteKey(PartialKey key) { if (key instanceof Key) { return (Key) key; } - PathElement leaf = key.getLeaf(); + Ancestor leaf = key.getLeaf(); if (leaf.hasId()) { return new Key(key, leaf.id()); } else if (leaf.hasName()) { @@ -215,17 +215,17 @@ static Key fromPb(DatastoreV1.Key keyPb) { return fromIncompleteKey(PartialKey.fromPb(keyPb)); } - private static ImmutableList newPath(PartialKey key, String name) { - ImmutableList.Builder path = ImmutableList.builder(); + private static ImmutableList newPath(PartialKey key, String name) { + ImmutableList.Builder path = ImmutableList.builder(); path.addAll(key.ancestors()); - path.add(new PathElement(key.kind(), name)); + path.add(new Ancestor(key.kind(), name)); return path.build(); } - private static ImmutableList newPath(PartialKey key, long id) { - ImmutableList.Builder path = ImmutableList.builder(); + private static ImmutableList newPath(PartialKey key, long id) { + ImmutableList.Builder path = ImmutableList.builder(); path.addAll(key.ancestors()); - path.add(new PathElement(key.kind(), id)); + path.add(new Ancestor(key.kind(), id)); return path.build(); } } diff --git a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java index 11b6af1f909c..8b9954717858 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java +++ b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java @@ -2,44 +2,59 @@ import static com.google.common.base.Preconditions.checkNotNull; +import com.google.gcloud.datastore.PartialKey.Ancestor; + /** - * An helper for creating keys for a specific {@link DatastoreService}/dataset. + * An helper for creating keys for a specific {@link DatastoreService}, + * using its associated dataset and namespace. */ -public final class KeyBuilder { +public final class KeyBuilder extends PartialKey.Builder { - private final PartialKey.Builder delegate; private final DatastoreService service; /** * Constructing a KeyBuilder. */ public KeyBuilder(DatastoreService service, String kind) { - this.service = checkNotNull(service); - delegate = new PartialKey.Builder(service.getOptions().dataset(), kind); - delegate.namespace(service.getOptions().defaultNamespace()); + super(checkNotNull(service).getOptions().dataset(), kind); + this.service = service; + namespace(service.getOptions().namespace()); } - public KeyBuilder addToPath(String kind, String name) { - delegate.addAncestor(kind, name); + @Override + public KeyBuilder kind(String kind) { + super.kind(kind); return this; } - public KeyBuilder addToPath(String kind, long id) { - delegate.addAncestor(kind, id); + @Override + public KeyBuilder addAncestor(String kind, String name) { + super.addAncestor(kind, name); return this; } + @Override + public KeyBuilder addAncestor(String kind, long id) { + super.addAncestor(kind, id); + return this; + } + + @Override public KeyBuilder namespace(String namespace) { - delegate.namespace(namespace); + super.namespace(namespace); return this; } - /** - * Builds a key with a newly allocated id. - * @throws DatastoreServiceException if allocation failed. - */ - public Key allocateIdAndBuild() { - return service.allocateId(build()); + @Override + public KeyBuilder addAncestor(Ancestor... ancestor) { + super.addAncestor(ancestor); + return this; + } + + @Override + public KeyBuilder addAncestors(Iterable ancestors) { + super.addAncestors(ancestors); + return this; } public Key build(String name) { @@ -58,7 +73,11 @@ public Key build(long id) { .build(); } - public PartialKey build() { - return delegate.build(); + /** + * Builds a key with a newly allocated id. + * @throws DatastoreServiceException if allocation failed. + */ + public Key allocateIdAndBuild() { + return service.allocateId(build()); } } diff --git a/src/main/java/com/google/gcloud/datastore/NullValue.java b/src/main/java/com/google/gcloud/datastore/NullValue.java index 5d77c333a54f..fb60ab20042f 100644 --- a/src/main/java/com/google/gcloud/datastore/NullValue.java +++ b/src/main/java/com/google/gcloud/datastore/NullValue.java @@ -51,7 +51,7 @@ public Builder set(Void value) { } public NullValue() { - this(new Builder().indexed(true)); + this(new Builder()); } NullValue(Builder builder) { diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index 7827b66dcd33..8f7f0b085739 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -1,9 +1,9 @@ package com.google.gcloud.datastore; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.gcloud.datastore.DatastoreServiceOptions.validateDataset; +import static com.google.gcloud.datastore.DatastoreServiceOptions.validateNamespace; import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Preconditions; @@ -26,9 +26,9 @@ public class PartialKey extends Serializable { private final transient String dataset; private final transient String namespace; - private final transient ImmutableList path; + private final transient ImmutableList ancestors; - public static final class PathElement extends Serializable { + public static final class Ancestor extends Serializable { private static final long serialVersionUID = -7968078857690784595L; @@ -36,17 +36,17 @@ public static final class PathElement extends Serializable path = new LinkedList<>(); + private final List ancestors = new LinkedList<>(); - private static final String DEFAULT_NAMESPACE = ""; private static final int MAX_PATH = 100; public Builder(String dataset, String kind) { @@ -138,33 +137,33 @@ public Builder(String dataset, String kind) { public Builder(Key parent, String kind) { dataset = parent.dataset(); namespace = parent.namespace(); - path.addAll(parent.ancestors()); - path.add(parent.getLeaf()); + ancestors.addAll(parent.ancestors()); + ancestors.add(parent.getLeaf()); this.kind = kind; } public Builder addAncestor(String kind, long id) { checkArgument(id != 0, "id must not be equal to zero"); - return addAncestor(new PathElement(kind, id)); + return addAncestor(new Ancestor(kind, id)); } public Builder addAncestor(String kind, String name) { checkArgument(Strings.isNullOrEmpty(name) , "name must not be empty or null"); checkArgument(name.length() <= 500, "name must not exceed 500 characters"); - return addAncestor(new PathElement(kind, name)); + return addAncestor(new Ancestor(kind, name)); } - public Builder addAncestor(PathElement... ancestor) { - Preconditions.checkState(path.size() + ancestor.length <= MAX_PATH, + public Builder addAncestor(Ancestor... ancestor) { + Preconditions.checkState(ancestors.size() + ancestor.length <= MAX_PATH, "path can have at most 100 elements"); - for (PathElement pathElement : ancestor) { - path.add(pathElement); + for (Ancestor pathElement : ancestor) { + ancestors.add(pathElement); } return this; } - public Builder addAncestors(Iterable ancestors) { - for (PathElement pathElement : ancestors) { + public Builder addAncestors(Iterable ancestors) { + for (Ancestor pathElement : ancestors) { addAncestor(pathElement); } return this; @@ -182,7 +181,7 @@ private String validateKind(String kind) { } public Builder clearPath() { - path.clear(); + ancestors.clear(); return this; } @@ -192,23 +191,23 @@ public Builder dataset(String dataset) { } public Builder namespace(String namespace) { - this.namespace = checkNotNull(namespace); + this.namespace = validateNamespace(namespace); return this; } public PartialKey build() { - PathElement leaf = new PathElement(kind); - ImmutableList pathList = - ImmutableList.builder().addAll(path).add(leaf).build(); + Ancestor leaf = new Ancestor(kind); + ImmutableList pathList = + ImmutableList.builder().addAll(ancestors).add(leaf).build(); return new PartialKey(dataset, namespace, pathList); } } - PartialKey(String dataset, String namespace, ImmutableList path) { + PartialKey(String dataset, String namespace, ImmutableList path) { checkState(!path.isEmpty(), "path must not be empty"); this.dataset = dataset; this.namespace = namespace; - this.path = path; + this.ancestors = path; } /** @@ -219,7 +218,7 @@ public String dataset() { } /** - * Returns the key's namespace. + * Returns the key's namespace or {@code null} if not provided. */ public String namespace() { return namespace; @@ -228,8 +227,8 @@ public String namespace() { /** * Returns an immutable list with the key's ancestors. */ - public List ancestors() { - return path.subList(0, path.size() - 1); + public List ancestors() { + return ancestors.subList(0, ancestors.size() - 1); } /** @@ -259,7 +258,7 @@ public Key toKey(long id) { @Override public int hashCode() { - return Objects.hash(dataset, namespace, path); + return Objects.hash(dataset, namespace, ancestors); } @Override @@ -270,26 +269,30 @@ public boolean equals(Object obj) { PartialKey other = (PartialKey) obj; return Objects.equals(dataset, other.dataset) && Objects.equals(namespace, other.namespace) - && Objects.equals(path, other.path); + && Objects.equals(ancestors, other.ancestors); } @Override protected DatastoreV1.Key toPb() { + return toPb(this).build(); + } + + static DatastoreV1.Key.Builder toPb(PartialKey key) { DatastoreV1.Key.Builder keyPb = DatastoreV1.Key.newBuilder(); DatastoreV1.PartitionId.Builder partitionIdPb = DatastoreV1.PartitionId.newBuilder(); - if (dataset != null) { - partitionIdPb.setDatasetId(dataset); + if (key.dataset != null) { + partitionIdPb.setDatasetId(key.dataset); } - if (namespace != null) { - partitionIdPb.setNamespace(namespace); + if (key.namespace != null) { + partitionIdPb.setNamespace(key.namespace); } if (partitionIdPb.hasDatasetId() || partitionIdPb.hasNamespace()) { keyPb.setPartitionId(partitionIdPb.build()); } - for (PathElement pathEntry : path) { + for (Ancestor pathEntry : key.ancestors) { keyPb.addPathElement(pathEntry.toPb()); } - return keyPb.build(); + return keyPb; } @Override @@ -309,14 +312,14 @@ static PartialKey fromPb(DatastoreV1.Key keyPb) { namespace = partitionIdPb.getNamespace(); } } - ImmutableList.Builder pathBuilder = ImmutableList.builder(); + ImmutableList.Builder pathBuilder = ImmutableList.builder(); for (DatastoreV1.Key.PathElement pathElementPb : keyPb.getPathElementList()) { - pathBuilder.add(PathElement.fromPb(pathElementPb)); + pathBuilder.add(Ancestor.fromPb(pathElementPb)); } return new PartialKey(dataset, namespace, pathBuilder.build()); } - PathElement getLeaf() { - return path.get(path.size() - 1); + Ancestor getLeaf() { + return ancestors.get(ancestors.size() - 1); } } diff --git a/src/main/java/com/google/gcloud/datastore/package-info.java b/src/main/java/com/google/gcloud/datastore/package-info.java new file mode 100644 index 000000000000..a5ec1bad4c05 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/package-info.java @@ -0,0 +1,31 @@ +/** + * A client to the Google Cloud Datastore. + * Typical usage would be: + *

 {@code
+ * DatastoreServiceOptions options = new DatastoreServiceOptions.Builder().dataset(DATASET).build();
+ * DatastoreService datastore = DatastoreServiceFactory.getDefault(options);
+ * KeyBuilder keyBuilder = datastore.newKeyBuilder(kind);
+ * Key key = keyBuilder.build(keyName);
+ * Entity entity = datastore.get(key);
+ * if (entity == null) {
+ *   entity = new Entity.Builder(key)
+ *       .setProperty("name", new StringValue("John Do"))
+ *       .setProperty("age", new LongValue.Builder(100).indexed(false).build())
+ *       .setProperty("updated", new BooleanValue(false))
+ *       .build();
+ *   datastore.put(entity);
+ * } else {
+ *   BooleanValue updated = entity.property("updated");
+ *   if (!updated.get()) {
+ *     entity = entity.builder()
+ *         .setProperty("updated", new BooleanValue(true))
+ *         .removeProperty("old_property")
+ *         .setProperty("new_property", new DoubleValue(1.1))
+ *         .build();
+ *     datastore.update(entity);
+ *   }
+ * }
+ * } 
+ * + */ +package com.google.gcloud.datastore; diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index fb00bb4dfa44..61debb6ead85 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -2,15 +2,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.google.api.services.datastore.client.LocalDevelopmentDatastore; -import com.google.api.services.datastore.client.LocalDevelopmentDatastoreException; - -import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -30,33 +27,43 @@ public class DatastoreServiceTest { private static final Key KEY1 = PARTIAL_KEY1.toKey("name"); private static final Key KEY2 = new Key.Builder(KEY1, KIND2, 1).build(); private static final Key KEY3 = KEY2.builder().name("bla").build(); + private static final PartialEntity PARTIAL_ENTITY1 = new PartialEntity.Builder(PARTIAL_KEY2) + .setProperty("str", STR_VALUE) + .setProperty("bool", BOOL_VALUE) + .build(); private static final Entity ENTITY1 = new Entity.Builder(KEY1) .setProperty("str", STR_VALUE) .setProperty("bool", BOOL_VALUE) + .setProperty("partial1", new PartialEntityValue(PARTIAL_ENTITY1)) .build(); private static final Entity ENTITY2 = new Entity.Builder(KEY2, ENTITY1) .removeProperty("str") .setProperty("null", NULL_VALUE) .build(); + private static final Entity ENTITY3 = new Entity.Builder(KEY3, ENTITY1) + .removeProperty("str") + .setProperty("null", NULL_VALUE) + .setProperty("partial2", new PartialEntityValue(ENTITY1)) + .build(); private DatastoreServiceOptions options; private DatastoreService datastore; - private LocalDevelopmentDatastore localDatastore; @Before - public void setUp() throws LocalDevelopmentDatastoreException { + public void setUp() { + // TODO(ozarov): document that this test depends on a local gcd running. + // gcd.sh start dataset1 + // reference: https://cloud.google.com/datastore/docs/tools/devserver + // Or even better, using a "GCE_HOME" param/env initiate and destroy the server + // before and after tests via ant or maven options = new DatastoreServiceOptions.Builder() .dataset(DATASET) - .host("http://localhost:8080/") + .host("http://localhost:8080") .build(); - datastore = DatastoreServiceFactory.Mode.TESTING.get(options); - localDatastore = (LocalDevelopmentDatastore) ((DatastoreServiceImpl) datastore).datastore(); - localDatastore.start("/usr/local/gcd-sdk", DATASET); - } - - @After - public void tearDown() throws LocalDevelopmentDatastoreException { - localDatastore.stop(); + datastore = DatastoreServiceFactory.getDefault(options); + // Prepare data for testing + datastore.delete(KEY1, KEY2, KEY3); + datastore.add(ENTITY1, ENTITY2); } @Test @@ -76,70 +83,139 @@ public void testNewBatchWriter() { @Test public void testAllocateId() { - fail("Not yet implemented"); + KeyBuilder keyBuilder = datastore.newKeyBuilder(KIND1); + PartialKey pk1 = keyBuilder.build(); + Key key1 = keyBuilder.allocateIdAndBuild(); + assertEquals(key1.dataset(), pk1.dataset()); + assertEquals(key1.namespace(), pk1.namespace()); + assertEquals(key1.ancestors(), pk1.ancestors()); + assertEquals(key1.kind(), pk1.kind()); + assertTrue(key1.hasId()); + assertFalse(key1.hasName()); + assertEquals(pk1.toKey(key1.id()), key1); + + Key key2 = datastore.allocateId(pk1); + assertNotEquals(key1, key2); + assertEquals(pk1.toKey(key2.id()), key2); + + Key key3 = datastore.allocateId(key1); + assertNotEquals(key1, key3); + assertEquals(pk1.toKey(key3.id()), key3); } @Test public void testAllocateIds() { - fail("Not yet implemented"); + KeyBuilder keyBuilder = datastore.newKeyBuilder(KIND1); + PartialKey key1 = keyBuilder.build(); + PartialKey key2 = keyBuilder.kind(KIND2).addAncestor(KIND1, 10).build(); + Iterator result = datastore.allocateIds(key1, key2); + Key key = result.next(); + assertEquals(key1.toKey(key.id()), key); + key = result.next(); + assertEquals(key2.toKey(key.id()), key); + assertFalse(result.hasNext()); } @Test - public void testAddAndGet() { - Entity entity = datastore.get(KEY1); + public void testGet() { + Entity entity = datastore.get(KEY3); assertNull(entity); - datastore.add(ENTITY1); - entity = datastore.get(KEY1); StringValue value1 = entity.property("str"); BooleanValue value2 = entity.property("bool"); + PartialEntityValue value3 = entity.property("partial1"); assertEquals(value1, STR_VALUE); assertEquals(value2, BOOL_VALUE); - assertEquals(2, entity.propertyNames().size()); + assertEquals(value3, new PartialEntityValue(PARTIAL_ENTITY1)); + assertEquals(3, entity.propertyNames().size()); assertTrue(entity.propertyNames().contains("str")); assertTrue(entity.propertyNames().contains("bool")); assertFalse(entity.hasProperty("bla")); } @Test - public void testAddAndGetArray() { - Iterator result = datastore.get(KEY1, KEY2); - assertNull(result.next()); - assertNull(result.next()); - assertFalse(result.hasNext()); - - populateDatastore(); - - result = datastore.get(KEY1, KEY1.builder().name("bla").build(), KEY2); + public void testGetArray() { + Iterator result = datastore.get(KEY1, KEY1.builder().name("bla").build(), KEY2); assertEquals(ENTITY1, result.next()); assertNull(result.next()); assertEquals(ENTITY2, result.next()); assertFalse(result.hasNext()); } - private void populateDatastore() { - datastore.add(ENTITY1, ENTITY2); - } - @Test public void testAdd() { - fail("Not yet implemented"); + Iterator keys = datastore.get(ENTITY1.key(), ENTITY3.key()); + assertEquals(ENTITY1, keys.next()); + assertNull(keys.next()); + assertFalse(keys.hasNext()); + + try { + datastore.add(ENTITY1); + } catch (DatastoreServiceException expected) { + // expected; + } + datastore.add(ENTITY3); + assertEquals(ENTITY3, datastore.get(ENTITY3.key())); } @Test public void testUpdate() { - fail("Not yet implemented"); + Iterator keys = datastore.get(ENTITY1.key(), ENTITY3.key()); + assertEquals(ENTITY1, keys.next()); + assertNull(keys.next()); + assertFalse(keys.hasNext()); + + try { + datastore.update(ENTITY3); + } catch (DatastoreServiceException expected) { + // expected; + } + datastore.add(ENTITY3); + assertEquals(ENTITY3, datastore.get(ENTITY3.key())); + Entity entity3 = ENTITY3.builder() + .clearProperties() + .setProperty("bla", new NullValue()) + .build(); + assertNotEquals(ENTITY3, entity3); + datastore.update(entity3); + assertEquals(entity3, datastore.get(ENTITY3.key())); } @Test public void testPut() { - fail("Not yet implemented"); + Iterator keys = datastore.get(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()); + assertEquals(ENTITY1, keys.next()); + assertEquals(ENTITY2, keys.next()); + assertNull(keys.next()); + assertFalse(keys.hasNext()); + + Entity entity2 = ENTITY2.builder() + .clearProperties() + .setProperty("bla", new NullValue()) + .build(); + assertNotEquals(ENTITY2, entity2); + datastore.put(ENTITY3, ENTITY1, entity2); + keys = datastore.get(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()); + assertEquals(ENTITY1, keys.next()); + assertEquals(entity2, keys.next()); + assertEquals(ENTITY3, keys.next()); + assertFalse(keys.hasNext()); } @Test public void testDelete() { - fail("Not yet implemented"); + Iterator keys = datastore.get(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()); + assertEquals(ENTITY1, keys.next()); + assertEquals(ENTITY2, keys.next()); + assertNull(keys.next()); + assertFalse(keys.hasNext()); + datastore.delete(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()); + keys = datastore.get(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()); + assertNull(keys.next()); + assertNull(keys.next()); + assertNull(keys.next()); + assertFalse(keys.hasNext()); } @Test From 71a3478d21e39d21a933753ad7e03aea679613e2 Mon Sep 17 00:00:00 2001 From: aozarov Date: Thu, 4 Dec 2014 18:55:04 -0800 Subject: [PATCH 029/771] adding batch and transaction --- .../gcloud/datastore/BatchWriteOption.java | 17 ++- .../google/gcloud/datastore/BatchWriter.java | 14 ++ .../gcloud/datastore/BatchWriterImpl.java | 139 ++++++++++++++++++ .../gcloud/datastore/DatastoreService.java | 8 +- .../datastore/DatastoreServiceImpl.java | 22 ++- .../gcloud/datastore/DatastoreWriter.java | 4 +- .../gcloud/datastore/TransactionImpl.java | 64 ++++++++ .../gcloud/datastore/TransactionOption.java | 49 +++--- 8 files changed, 276 insertions(+), 41 deletions(-) create mode 100644 src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java create mode 100644 src/main/java/com/google/gcloud/datastore/TransactionImpl.java diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java b/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java index cda7bf7be16f..b860b118f2fa 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java +++ b/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java @@ -6,10 +6,6 @@ public abstract class BatchWriteOption implements Serializable { private static final long serialVersionUID = -3932758377282659839L; - BatchWriteOption() { - // package protected - } - public static final class ForceWrites extends BatchWriteOption { private static final long serialVersionUID = 2555054296046232799L; @@ -20,11 +16,22 @@ public ForceWrites(boolean force) { this.force = force; } - public boolean isForce() { + public boolean force() { return force; } + + @Override + void apply(BatchWriterImpl batchWriter) { + batchWriter.apply(this); + } } + BatchWriteOption() { + // package protected + } + + abstract void apply(BatchWriterImpl batchWriter); + public static ForceWrites forceWrites() { return new ForceWrites(true); } diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriter.java b/src/main/java/com/google/gcloud/datastore/BatchWriter.java index c8e1056b21d9..889ff425a1ab 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchWriter.java +++ b/src/main/java/com/google/gcloud/datastore/BatchWriter.java @@ -2,6 +2,20 @@ public interface BatchWriter extends DatastoreWriter { + /** + * {@inheritDoc} + * @throws DatastoreServiceException if a given entity already added to this batch + */ + @Override + void add(Entity... entity); + + /** + * {@inheritDoc} + * @throws DatastoreServiceException if an entity is marked for deletion in this batch + */ + @Override + void update(Entity... entity); + /** * Submit the batch to the Datastore. * diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java new file mode 100644 index 000000000000..f80c0e17af2f --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java @@ -0,0 +1,139 @@ +package com.google.gcloud.datastore; + +import static com.google.gcloud.datastore.DatastoreServiceException.Code.FAILED_PRECONDITION; + +import com.google.api.services.datastore.DatastoreV1; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; + +class BatchWriterImpl implements BatchWriter { + + private final LinkedHashMap toAdd = new LinkedHashMap<>(); + private final LinkedHashMap toUpdate = new LinkedHashMap<>(); + private final LinkedHashMap toPut = new LinkedHashMap<>(); + private final LinkedHashSet toDelete = new LinkedHashSet<>(); + private final DatastoreServiceImpl datastore; + + private boolean force; + protected boolean isValid = true; + + BatchWriterImpl(DatastoreServiceImpl datastore, BatchWriteOption... options) { + this.datastore = datastore; + force = datastore.getOptions().force(); + for (BatchWriteOption option : options) { + option.apply(this); + } + } + + // Apply all valid options + + void apply(BatchWriteOption.ForceWrites forceOptions) { + this.force = forceOptions.force(); + } + + void apply(BatchWriteOption other) { + // dont care + } + + //////////////////// + + DatastoreServiceException newBatchFailure(Entity entity, String msg) { + isValid = false; + return new DatastoreServiceException(FAILED_PRECONDITION, + new RuntimeException("Entity with the key " + entity.key() + " " + msg)); + } + + protected void checkValid() { + if (!isValid) { + throw new DatastoreServiceException(FAILED_PRECONDITION, + new RuntimeException("BatchWriter is in an invalid state")); + } + } + + @Override + public void add(Entity... entities) { + checkValid(); + for (Entity entity : entities) { + Key key = entity.key(); + if (toAdd.containsKey(key) || toUpdate.containsKey(key) || toPut.containsKey(key)) { + throw newBatchFailure(entity, "was already added or updated in this batch"); + } + if (toDelete.remove(key)) { + toPut.put(key, entity); + } else { + toAdd.put(key, entity); + } + } + } + + @Override + public void update(Entity... entities) { + checkValid(); + for (Entity entity : entities) { + Key key = entity.key(); + if (toDelete.contains(key)) { + throw newBatchFailure(entity, "was alredy deleted in this batch"); + } + if (toAdd.remove(key) != null || toPut.containsKey(key)) { + toPut.put(key, entity); + } else { + toUpdate.put(key, entity); + } + } + } + + @Override + public void put(Entity... entities) { + checkValid(); + for (Entity entity : entities) { + Key key = entity.key(); + toAdd.remove(key); + toUpdate.remove(key); + toDelete.remove(key); + toPut.put(key, entity); + } + } + + @Override + public void delete(Key... keys) { + checkValid(); + for (Key key : keys) { + toAdd.remove(key); + toUpdate.remove(key); + toPut.remove(key); + toDelete.add(key); + } + } + + @Override + public void submit() { + checkValid(); + DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); + for (Entity entity : toAdd.values()) { + mutationPb.addInsert(entity.toPb()); + } + for (Entity entity : toUpdate.values()) { + mutationPb.addUpdate(entity.toPb()); + } + for (Entity entity : toPut.values()) { + mutationPb.addUpsert(entity.toPb()); + } + for (Key key : toDelete) { + mutationPb.addDelete(key.toPb()); + } + if (force) { + mutationPb.setForce(force); + } + DatastoreV1.CommitRequest.Builder requestPb = newCommitRequest(); + requestPb.setMutation(mutationPb); + datastore.comitMutation(requestPb); + isValid = false; + } + + protected DatastoreV1.CommitRequest.Builder newCommitRequest() { + DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder(); + requestPb.setMode(DatastoreV1.CommitRequest.Mode.NON_TRANSACTIONAL); + return requestPb; + } +} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index 37fb52ad701f..8a0472e4e995 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -49,28 +49,28 @@ public interface DatastoreService extends DatastoreReader, DatastoreWriter { Iterator allocateIds(PartialKey... key); /** - * @see DatastoreWriter#add(Entity...) + * {@inheritDoc} * @throws DatastoreServiceExcepiton upon failure */ @Override void add(Entity... entity); /** - * @see DatastoreWriter#update(Entity...) + * {@inheritDoc} * @throws DatastoreServiceExcepiton upon failure */ @Override void update(Entity... entity); /** - * @see DatastoreWriter#put(Entity...) + * {@inheritDoc} * @throws DatastoreServiceExcepiton upon failure */ @Override void put(Entity... entity); /** - * @see DatastoreWriter#delete(Key...) + * {@inheritDoc} * @throws DatastoreServiceExcepiton upon failure */ @Override diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index 4dcae9612568..6fdd57f622ce 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -1,9 +1,11 @@ package com.google.gcloud.datastore; import com.google.api.services.datastore.DatastoreV1; +import com.google.api.services.datastore.DatastoreV1.BeginTransactionResponse; import com.google.api.services.datastore.client.Datastore; import com.google.api.services.datastore.client.DatastoreException; import com.google.common.collect.AbstractIterator; +import com.google.protobuf.ByteString; import java.util.HashMap; import java.util.Iterator; @@ -27,14 +29,21 @@ public DatastoreServiceOptions getOptions() { @Override public Transaction newTransaction(TransactionOption... transactionOption) { - // TODO Auto-generated method stub - return null; + return new TransactionImpl(this, transactionOption); + } + + ByteString requestTransactionId(DatastoreV1.BeginTransactionRequest.Builder requestPb) { + try { + BeginTransactionResponse responsePb = datastore.beginTransaction(requestPb.build()); + return responsePb.getTransaction(); + } catch (DatastoreException e) { + throw DatastoreServiceException.translateAndPropagate(e); + } } @Override public BatchWriter newBatchWriter(BatchWriteOption... batchWriteOption) { - // TODO Auto-generated method stub - return null; + return new BatchWriterImpl(this, batchWriteOption); } @Override @@ -123,7 +132,10 @@ private void comitMutation(DatastoreV1.Mutation.Builder mutationPb) { } DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder(); requestPb.setMode(DatastoreV1.CommitRequest.Mode.NON_TRANSACTIONAL); - requestPb.setMutation(mutationPb.build()); + requestPb.setMutation(mutationPb); + } + + void comitMutation(DatastoreV1.CommitRequest.Builder requestPb) { try { datastore.commit(requestPb.build()); } catch (DatastoreException e) { diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java b/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java index cfb864744099..5c87a49424ba 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java @@ -7,13 +7,13 @@ public interface DatastoreWriter { /** * A Datastore add operation. - * The operation will fail if an entity already exists. + * The operation will fail if an entity with the same key already exists. */ void add(Entity... entity); /** * A Datastore update operation. - * The operation will fail if an entity does not already exist. + * The operation will fail if an entity with the same key does not already exist. */ void update(Entity... entity); diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java new file mode 100644 index 000000000000..61db951e343c --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java @@ -0,0 +1,64 @@ +package com.google.gcloud.datastore; + +import com.google.api.services.datastore.DatastoreV1; +import com.google.gcloud.datastore.TransactionOption.IsolationLevel; +import com.google.protobuf.ByteString; + +import java.util.Iterator; + +public final class TransactionImpl extends BatchWriterImpl implements Transaction { + + private final ByteString transaction; + private IsolationLevel isolationLevel; + + TransactionImpl(DatastoreServiceImpl datastore, TransactionOption... options) { + super(datastore, options); + DatastoreV1.BeginTransactionRequest.Builder requestPb = + DatastoreV1.BeginTransactionRequest.newBuilder(); + if (isolationLevel != null) { + requestPb.setIsolationLevel(isolationLevel.level().toPb()); + } + transaction = datastore.requestTransactionId(requestPb); + } + + void apply(IsolationLevel isolationLevel) { + // TODO(ozarov): validate that this concept actually works!!! + this.isolationLevel = isolationLevel; + } + + @Override + public Entity get(Key key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Iterator get(Key... key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public QueryResult runQuery(Query query) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void commit() { + submit(); + } + + @Override + public void rollback() { + isValid = false; + } + + @Override + protected DatastoreV1.CommitRequest.Builder newCommitRequest() { + DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder(); + requestPb.setMode(DatastoreV1.CommitRequest.Mode.TRANSACTIONAL); + requestPb.setTransaction(transaction); + return requestPb; + } +} diff --git a/src/main/java/com/google/gcloud/datastore/TransactionOption.java b/src/main/java/com/google/gcloud/datastore/TransactionOption.java index 50bd2aac2869..56e2df445b17 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionOption.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionOption.java @@ -1,15 +1,11 @@ package com.google.gcloud.datastore; -import java.io.Serializable; +import com.google.api.services.datastore.DatastoreV1; -public abstract class TransactionOption implements Serializable { - - private static final long serialVersionUID = -1862234444015690375L; +public abstract class TransactionOption extends BatchWriteOption { - TransactionOption() { - // package protected - } + private static final long serialVersionUID = -1862234444015690375L; public static final class IsolationLevel extends TransactionOption { @@ -18,31 +14,38 @@ public static final class IsolationLevel extends TransactionOption { private final Level level; public enum Level { - SERIALIZABLE, SNAPSHOT; + + SERIALIZABLE(DatastoreV1.BeginTransactionRequest.IsolationLevel.SERIALIZABLE), + SNAPSHOT(DatastoreV1.BeginTransactionRequest.IsolationLevel.SNAPSHOT); + + private final DatastoreV1.BeginTransactionRequest.IsolationLevel levelPb; + + Level(DatastoreV1.BeginTransactionRequest.IsolationLevel levelPb) { + this.levelPb = levelPb; + } + + DatastoreV1.BeginTransactionRequest.IsolationLevel toPb() { + return levelPb; + } } public IsolationLevel(Level level) { this.level = level; } - public Level getLevel() { + + public Level level() { return level; } - } - - public static final class ForceWrites extends TransactionOption { - - private static final long serialVersionUID = 7448106703678852594L; - private final boolean force; - - public ForceWrites(boolean force) { - this.force = force; + @Override + void apply(BatchWriterImpl batchWriter) { + batchWriter.apply(this); } + } - public boolean isForce() { - return force; - } + TransactionOption() { + // package protected } public static IsolationLevel serializable() { @@ -52,8 +55,4 @@ public static IsolationLevel serializable() { public static IsolationLevel snapshot() { return new IsolationLevel(IsolationLevel.Level.SNAPSHOT); } - - public static ForceWrites forceWrites() { - return new ForceWrites(true); - } } From 4a7eaf15e8ab0ab6f5e408c53ecb15feb5fc807e Mon Sep 17 00:00:00 2001 From: aozarov Date: Fri, 5 Dec 2014 12:22:43 -0800 Subject: [PATCH 030/771] more documenation and complete batch and transaction (no tests yet) --- .../gcloud/datastore/BatchWriteOption.java | 20 +- .../gcloud/datastore/BatchWriterImpl.java | 54 +++--- .../gcloud/datastore/DatastoreReader.java | 2 +- .../gcloud/datastore/DatastoreService.java | 2 +- .../datastore/DatastoreServiceException.java | 17 +- .../datastore/DatastoreServiceImpl.java | 180 +++++++++++------- .../google/gcloud/datastore/Transaction.java | 36 ++++ .../gcloud/datastore/TransactionImpl.java | 46 +++-- .../gcloud/datastore/TransactionOption.java | 5 - .../datastore/DatastoreServiceTest.java | 35 +++- 10 files changed, 260 insertions(+), 137 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java b/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java index b860b118f2fa..2fea6e95d893 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java +++ b/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java @@ -1,6 +1,9 @@ package com.google.gcloud.datastore; +import com.google.common.collect.ImmutableMap; + import java.io.Serializable; +import java.util.Map; public abstract class BatchWriteOption implements Serializable { @@ -19,20 +22,23 @@ public ForceWrites(boolean force) { public boolean force() { return force; } - - @Override - void apply(BatchWriterImpl batchWriter) { - batchWriter.apply(this); - } } BatchWriteOption() { // package protected } - abstract void apply(BatchWriterImpl batchWriter); - public static ForceWrites forceWrites() { return new ForceWrites(true); } + + static Map, BatchWriteOption> asImmutableMap( + BatchWriteOption... options) { + ImmutableMap.Builder, BatchWriteOption> builder = + ImmutableMap.builder(); + for (BatchWriteOption option : options) { + builder.put(option.getClass(), option); + } + return builder.build(); + } } diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java index f80c0e17af2f..89fbc60742e2 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java +++ b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java @@ -1,11 +1,13 @@ package com.google.gcloud.datastore; -import static com.google.gcloud.datastore.DatastoreServiceException.Code.FAILED_PRECONDITION; +import static com.google.gcloud.datastore.DatastoreServiceException.throwInvalidRequest; import com.google.api.services.datastore.DatastoreV1; +import com.google.gcloud.datastore.BatchWriteOption.ForceWrites; import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.Map; class BatchWriterImpl implements BatchWriter { @@ -13,51 +15,40 @@ class BatchWriterImpl implements BatchWriter { private final LinkedHashMap toUpdate = new LinkedHashMap<>(); private final LinkedHashMap toPut = new LinkedHashMap<>(); private final LinkedHashSet toDelete = new LinkedHashSet<>(); - private final DatastoreServiceImpl datastore; + private final boolean force; + protected final DatastoreServiceImpl datastore; - private boolean force; - protected boolean isValid = true; + private boolean wasSubmitted = false; BatchWriterImpl(DatastoreServiceImpl datastore, BatchWriteOption... options) { this.datastore = datastore; - force = datastore.getOptions().force(); - for (BatchWriteOption option : options) { - option.apply(this); + Map, BatchWriteOption> optionsMap = + BatchWriteOption.asImmutableMap(options); + if (optionsMap.containsKey(ForceWrites.class)) { + force = ((ForceWrites) optionsMap.get(ForceWrites.class)).force(); + } else { + force = datastore.getOptions().force(); } } - // Apply all valid options - - void apply(BatchWriteOption.ForceWrites forceOptions) { - this.force = forceOptions.force(); - } - - void apply(BatchWriteOption other) { - // dont care - } - - //////////////////// - - DatastoreServiceException newBatchFailure(Entity entity, String msg) { - isValid = false; - return new DatastoreServiceException(FAILED_PRECONDITION, - new RuntimeException("Entity with the key " + entity.key() + " " + msg)); - } - protected void checkValid() { - if (!isValid) { - throw new DatastoreServiceException(FAILED_PRECONDITION, - new RuntimeException("BatchWriter is in an invalid state")); + if (wasSubmitted) { + throwInvalidRequest(getName() + " was already submitted"); } } + protected String getName() { + return "batch"; + } + @Override public void add(Entity... entities) { checkValid(); for (Entity entity : entities) { Key key = entity.key(); if (toAdd.containsKey(key) || toUpdate.containsKey(key) || toPut.containsKey(key)) { - throw newBatchFailure(entity, "was already added or updated in this batch"); + throw throwInvalidRequest("Entity with the key %s was already added or updated in this " + + getName(), entity.key()); } if (toDelete.remove(key)) { toPut.put(key, entity); @@ -73,7 +64,8 @@ public void update(Entity... entities) { for (Entity entity : entities) { Key key = entity.key(); if (toDelete.contains(key)) { - throw newBatchFailure(entity, "was alredy deleted in this batch"); + throw throwInvalidRequest( + "Entity with the key %s was already deleted in this " + getName(), entity.key()); } if (toAdd.remove(key) != null || toPut.containsKey(key)) { toPut.put(key, entity); @@ -128,7 +120,7 @@ public void submit() { DatastoreV1.CommitRequest.Builder requestPb = newCommitRequest(); requestPb.setMutation(mutationPb); datastore.comitMutation(requestPb); - isValid = false; + wasSubmitted = true; } protected DatastoreV1.CommitRequest.Builder newCommitRequest() { diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java index 32b4dbd73461..ac2e048eff0f 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java @@ -20,7 +20,7 @@ public interface DatastoreReader { * * @throws DatastoreServiceException upon failure. */ - Iterator get(Key... key); + Iterator get(Key key, Key... others); /** * Submit a {@link Query} and returns its result. diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index 8a0472e4e995..b7633c49cd10 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -46,7 +46,7 @@ public interface DatastoreService extends DatastoreReader, DatastoreWriter { * @see #allocateId(PartialKey) * @throws DatastoreServiceExcepiton upon failure */ - Iterator allocateIds(PartialKey... key); + Iterator allocateId(PartialKey key, PartialKey... others); /** * {@inheritDoc} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java index 912efe4a4692..94c16966f1fb 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java @@ -69,9 +69,22 @@ public Code code() { * Translate DatastoreException to DatastoreServiceException based on their * HTTP error codes. This method will always throw a new DatastoreServiceException. * - * @throws DatastoreServiceException for every given DatastoreException + * @throws DatastoreServiceException every time */ - static DatastoreServiceException translateAndPropagate(DatastoreException exception) { + static DatastoreServiceException translateAndThrow(DatastoreException exception) { throw firstNonNull(HTTP_TO_CODE.get(exception.getCode()), Code.UNKNOWN).translate(exception); } + + /** + * Throw a DatastoreServiceException with {@code FAILED_PRECONDITION} code and the {@code msg} + * in a nested exception. + * + * @throws DatastoreServiceException every time + */ + static DatastoreServiceException throwInvalidRequest(String msg, Object... params) { + if (params.length > 0) { + msg = String.format(msg, params); + } + throw new DatastoreServiceException(Code.FAILED_PRECONDITION, new RuntimeException(msg)); + } } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index 6fdd57f622ce..69819b1c73f0 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -7,13 +7,19 @@ import com.google.common.collect.AbstractIterator; import com.google.protobuf.ByteString; +import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.Map; final class DatastoreServiceImpl implements DatastoreService { + static final Key[] EMPTY_KEY_ARRAY = {}; + static final PartialKey[] EMPTY_PARTIAL_KEY_ARRAY = {}; + private final DatastoreServiceOptions options; private final Datastore datastore; @@ -28,17 +34,8 @@ public DatastoreServiceOptions getOptions() { } @Override - public Transaction newTransaction(TransactionOption... transactionOption) { - return new TransactionImpl(this, transactionOption); - } - - ByteString requestTransactionId(DatastoreV1.BeginTransactionRequest.Builder requestPb) { - try { - BeginTransactionResponse responsePb = datastore.beginTransaction(requestPb.build()); - return responsePb.getTransaction(); - } catch (DatastoreException e) { - throw DatastoreServiceException.translateAndPropagate(e); - } + public KeyBuilder newKeyBuilder(String kind) { + return new KeyBuilder(this, kind); } @Override @@ -46,23 +43,29 @@ public BatchWriter newBatchWriter(BatchWriteOption... batchWriteOption) { return new BatchWriterImpl(this, batchWriteOption); } + @Override + public Transaction newTransaction(TransactionOption... transactionOption) { + return new TransactionImpl(this, transactionOption); + } + + @Override + public QueryResult runQuery(Query query) { + // TODO To implement + throw new RuntimeException("Not implemented yet"); + } + + @Override public Key allocateId(PartialKey key) { - return allocateIds(key).next(); + return allocateId(key, EMPTY_PARTIAL_KEY_ARRAY).next(); } @Override - public Iterator allocateIds(PartialKey... key) { + public Iterator allocateId(PartialKey key, PartialKey... others) { DatastoreV1.AllocateIdsRequest.Builder requestPb = DatastoreV1.AllocateIdsRequest.newBuilder(); - for (PartialKey k : key) { - if (k.getLeaf().nameOrId() != null) { - // if key is full remove the id or name part - k = new PartialKey.Builder(k.dataset(), k.kind()) - .namespace(k.namespace()) - .addAncestors(k.ancestors()) - .build(); - } - requestPb.addKey(k.toPb()); + requestPb.addKey(trimNameOrId(key).toPb()); + for (PartialKey other : others) { + requestPb.addKey(trimNameOrId(other).toPb()); } // TODO(ozarov): will need to populate "force" after b/18594027 is fixed. try { @@ -79,19 +82,39 @@ protected Key computeNext() { } }; } catch (DatastoreException e) { - throw DatastoreServiceException.translateAndPropagate(e); + throw DatastoreServiceException.translateAndThrow(e); } } + private PartialKey trimNameOrId(PartialKey key) { + if (key.getLeaf().nameOrId() == null) { + return key; + } + return new PartialKey.Builder(key.dataset(), key.kind()) + .namespace(key.namespace()) + .addAncestors(key.ancestors()) + .build(); + } + @Override public Entity get(Key key) { - return get(new Key[]{key}).next(); + return get(key, EMPTY_KEY_ARRAY).next(); } @Override - public Iterator get(final Key... key) { + public Iterator get(Key key, Key... others) { + return get(null, key, others); + } + + Iterator get(DatastoreV1.ReadOptions readOptionsPb, final Key key, final Key... others) { DatastoreV1.LookupRequest.Builder requestPb = DatastoreV1.LookupRequest.newBuilder(); - for (Key k : key) { + if (readOptionsPb != null) { + requestPb.setReadOptions(readOptionsPb); + } + LinkedHashSet dedupKeys = new LinkedHashSet<>(); + dedupKeys.add(key); + dedupKeys.addAll(Arrays.asList(others)); + for (Key k : dedupKeys) { requestPb.addKey(k.toPb()); } try { @@ -102,86 +125,109 @@ public Iterator get(final Key... key) { result.put(entity.key(), entity); } return new AbstractIterator() { - int index; + int index = -2; @Override protected Entity computeNext() { - if (index < key.length) { - return result.get(key[index++]); + ++index; + if (index < 0) { + return result.get(key); + } + if (index < others.length) { + return result.get(others[index]); } return endOfData(); } }; } catch (DatastoreException e) { - throw DatastoreServiceException.translateAndPropagate(e); + throw DatastoreServiceException.translateAndThrow(e); } } @Override - public void add(Entity... entity) { + public void add(Entity... entities) { DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); - for (Entity e : entity) { - mutationPb.addInsert(e.toPb()); + LinkedHashSet keys = new LinkedHashSet<>(); + for (Entity entity : entities) { + if (!keys.add(entity.key())) { + throw DatastoreServiceException.throwInvalidRequest( + "Duplicate entity with the key %s", entity.key()); + } + mutationPb.addInsert(entity.toPb()); } comitMutation(mutationPb); } - private void comitMutation(DatastoreV1.Mutation.Builder mutationPb) { - if (options.force()) { - mutationPb.setForce(true); - } - DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder(); - requestPb.setMode(DatastoreV1.CommitRequest.Mode.NON_TRANSACTIONAL); - requestPb.setMutation(mutationPb); - } - - void comitMutation(DatastoreV1.CommitRequest.Builder requestPb) { - try { - datastore.commit(requestPb.build()); - } catch (DatastoreException e) { - throw DatastoreServiceException.translateAndPropagate(e); - } - } - @Override - public void update(Entity... entity) { + public void update(Entity... entities) { DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); - for (Entity e : entity) { - mutationPb.addUpdate(e.toPb()); + LinkedHashMap dedupEntities = new LinkedHashMap<>(); + for (Entity entity : entities) { + dedupEntities.put(entity.key(), entity); + } + for (Entity entity : dedupEntities.values()) { + mutationPb.addUpdate(entity.toPb()); } comitMutation(mutationPb); } @Override - public void put(Entity... entity) { + public void put(Entity... entities) { DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); - for (Entity e : entity) { + LinkedHashMap dedupEntities = new LinkedHashMap<>(); + for (Entity entity : entities) { + dedupEntities.put(entity.key(), entity); + } + for (Entity e : dedupEntities.values()) { mutationPb.addUpsert(e.toPb()); } comitMutation(mutationPb); } @Override - public void delete(Key... key) { + public void delete(Key... keys) { DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); - for (Key k : key) { - mutationPb.addDelete(k.toPb()); + LinkedHashSet dedupKeys = new LinkedHashSet<>(Arrays.asList(keys)); + for (Key key : dedupKeys) { + mutationPb.addDelete(key.toPb()); } comitMutation(mutationPb); } - @Override - public KeyBuilder newKeyBuilder(String kind) { - return new KeyBuilder(this, kind); + private void comitMutation(DatastoreV1.Mutation.Builder mutationPb) { + if (options.force()) { + mutationPb.setForce(true); + } + DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder(); + requestPb.setMode(DatastoreV1.CommitRequest.Mode.NON_TRANSACTIONAL); + requestPb.setMutation(mutationPb); + comitMutation(requestPb); } - @Override - public QueryResult runQuery(Query query) { - // TODO Auto-generated method stub - return null; + void comitMutation(DatastoreV1.CommitRequest.Builder requestPb) { + try { + datastore.commit(requestPb.build()); + } catch (DatastoreException e) { + throw DatastoreServiceException.translateAndThrow(e); + } } - Datastore datastore() { - return datastore; + ByteString requestTransactionId(DatastoreV1.BeginTransactionRequest.Builder requestPb) { + try { + BeginTransactionResponse responsePb = datastore.beginTransaction(requestPb.build()); + return responsePb.getTransaction(); + } catch (DatastoreException e) { + throw DatastoreServiceException.translateAndThrow(e); + } + } + + void rollbackTransaction(ByteString transaction) { + DatastoreV1.RollbackRequest.Builder requestPb = DatastoreV1.RollbackRequest.newBuilder(); + requestPb.setTransaction(transaction); + try { + datastore.rollback(requestPb.build()); + } catch (DatastoreException e) { + throw DatastoreServiceException.translateAndThrow(e); + } } } diff --git a/src/main/java/com/google/gcloud/datastore/Transaction.java b/src/main/java/com/google/gcloud/datastore/Transaction.java index fd916960d78a..4138b8409d98 100644 --- a/src/main/java/com/google/gcloud/datastore/Transaction.java +++ b/src/main/java/com/google/gcloud/datastore/Transaction.java @@ -1,5 +1,7 @@ package com.google.gcloud.datastore; +import java.util.Iterator; + /** * A Google cloud datastore transaction. * @@ -7,6 +9,40 @@ */ public interface Transaction extends DatastoreReader, DatastoreWriter { + /** + * {@inheritDoc} + * The requested entity will be part of this Datastore transaction (so a commit is guaranteed + * to fail if entity was changed by others after it was seen by this transaction) but any + * write changes in this transaction will not be reflected by the returned entity. + * + * @throws DatastoreServiceException upon failure. + */ + @Override + Entity get(Key key); + + /** + * {@inheritDoc} + * The requested entities will be part of this Datastore transaction (so a commit is guaranteed + * to fail if any of the entities was changed by others after they were seen by this transaction) + * but any write changes in this transaction will not be reflected by the returned entities. + * + * @throws DatastoreServiceException upon failure. + */ + @Override + Iterator get(Key key, Key... others); + + /** + * {@inheritDoc} + * The entities returned by the result of this query will be part of this Datastore transaction + * (so a commit is guaranteed to fail if any of the entities was changed by others after the + * query was performed) but any write changes in this transaction will not be reflected by + * the result. + * + * @throws DatastoreServiceException upon failure. + */ + @Override + QueryResult runQuery(Query query); + /** * Commit the transaction. * diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java index 61db951e343c..17fe3e1339fc 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java @@ -1,47 +1,50 @@ package com.google.gcloud.datastore; +import static com.google.gcloud.datastore.DatastoreServiceException.throwInvalidRequest; + import com.google.api.services.datastore.DatastoreV1; import com.google.gcloud.datastore.TransactionOption.IsolationLevel; import com.google.protobuf.ByteString; import java.util.Iterator; +import java.util.Map; public final class TransactionImpl extends BatchWriterImpl implements Transaction { private final ByteString transaction; - private IsolationLevel isolationLevel; + private boolean wasRolledback; TransactionImpl(DatastoreServiceImpl datastore, TransactionOption... options) { super(datastore, options); DatastoreV1.BeginTransactionRequest.Builder requestPb = DatastoreV1.BeginTransactionRequest.newBuilder(); + Map, BatchWriteOption> optionsMap = + BatchWriteOption.asImmutableMap(options); + IsolationLevel isolationLevel = (IsolationLevel) optionsMap.get(IsolationLevel.class); if (isolationLevel != null) { requestPb.setIsolationLevel(isolationLevel.level().toPb()); } transaction = datastore.requestTransactionId(requestPb); } - void apply(IsolationLevel isolationLevel) { - // TODO(ozarov): validate that this concept actually works!!! - this.isolationLevel = isolationLevel; - } - @Override public Entity get(Key key) { - // TODO Auto-generated method stub - return null; + return get(key, DatastoreServiceImpl.EMPTY_KEY_ARRAY).next(); } @Override - public Iterator get(Key... key) { - // TODO Auto-generated method stub - return null; + public Iterator get(Key key, Key... others) { + checkValid(); + DatastoreV1.ReadOptions.Builder readOptionsPb = DatastoreV1.ReadOptions.newBuilder(); + readOptionsPb.setTransaction(transaction); + return datastore.get(readOptionsPb.build(), key, others); } @Override public QueryResult runQuery(Query query) { - // TODO Auto-generated method stub - return null; + checkValid(); + // TODO To implement + throw new RuntimeException("Not implemented yet"); } @Override @@ -51,7 +54,22 @@ public void commit() { @Override public void rollback() { - isValid = false; + super.checkValid(); + datastore.rollbackTransaction(transaction); + wasRolledback = true; + } + + @Override + protected String getName() { + return "transaction"; + } + + @Override + protected void checkValid() { + super.checkValid(); + if (wasRolledback) { + throwInvalidRequest(getName() + " was already rolledback"); + } } @Override diff --git a/src/main/java/com/google/gcloud/datastore/TransactionOption.java b/src/main/java/com/google/gcloud/datastore/TransactionOption.java index 56e2df445b17..0471929e7fb1 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionOption.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionOption.java @@ -37,11 +37,6 @@ public IsolationLevel(Level level) { public Level level() { return level; } - - @Override - void apply(BatchWriterImpl batchWriter) { - batchWriter.apply(this); - } } TransactionOption() { diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index 61debb6ead85..2c3914d2a4f6 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -11,7 +11,9 @@ import org.junit.Before; import org.junit.Test; +import java.util.HashMap; import java.util.Iterator; +import java.util.Map; public class DatastoreServiceTest { @@ -78,6 +80,12 @@ public void testNewTransaction() { @Test public void testNewBatchWriter() { + + fail("Not yet implemented"); + } + + @Test + public void testRunQuery() { fail("Not yet implemented"); } @@ -104,16 +112,25 @@ public void testAllocateId() { } @Test - public void testAllocateIds() { + public void testAllocateIdArray() { KeyBuilder keyBuilder = datastore.newKeyBuilder(KIND1); - PartialKey key1 = keyBuilder.build(); - PartialKey key2 = keyBuilder.kind(KIND2).addAncestor(KIND1, 10).build(); - Iterator result = datastore.allocateIds(key1, key2); - Key key = result.next(); - assertEquals(key1.toKey(key.id()), key); - key = result.next(); - assertEquals(key2.toKey(key.id()), key); - assertFalse(result.hasNext()); + PartialKey pKey1 = keyBuilder.build(); + PartialKey pKey2 = keyBuilder.kind(KIND2).addAncestor(KIND1, 10).build(); + Key key3 = keyBuilder.build("name"); + Key key4 = keyBuilder.build(1); + Iterator result = datastore.allocateId(pKey1, pKey2, key3, key4, pKey1, key3); + Map map = new HashMap<>(); + int count = 0; + while (result.hasNext()) { + map.put(++count, result.next()); + } + assertEquals(6, map.size()); + assertEquals(pKey1.toKey(map.get(1).id()), map.get(1)); + assertEquals(pKey1.toKey(map.get(5).id()), map.get(5)); + assertEquals(pKey2.toKey(map.get(2).id()), map.get(2)); + assertEquals(key3.builder().id(map.get(3).id()).build(), map.get(3)); + assertEquals(key3.builder().id(map.get(6).id()).build(), map.get(6)); + assertEquals(key4.builder().id(map.get(4).id()).build(), map.get(4)); } @Test From 1222d1e35a8add26788843c101530e30c74755c3 Mon Sep 17 00:00:00 2001 From: aozarov Date: Fri, 5 Dec 2014 12:45:59 -0800 Subject: [PATCH 031/771] Added some tests for batchwrite --- .../google/gcloud/datastore/PartialKey.java | 4 +- .../datastore/DatastoreServiceTest.java | 62 ++++++++++++++++--- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index 8f7f0b085739..d51cd19cd621 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -242,14 +242,14 @@ public Builder builder() { return new Builder(dataset(), kind()).namespace(namespace()).addAncestors(ancestors()); } - public Key toKey(String name) { + public Key newKey(String name) { return new Key.Builder(dataset(), kind(), name) .namespace(namespace()) .addAncestors(ancestors()) .build(); } - public Key toKey(long id) { + public Key newKey(long id) { return new Key.Builder(dataset(), kind(), id) .namespace(namespace()) .addAncestors(ancestors()) diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index 2c3914d2a4f6..4e9c23b421c1 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -26,7 +26,7 @@ public class DatastoreServiceTest { new BooleanValue.Builder(false).indexed(false).build(); private static final PartialKey PARTIAL_KEY1 = new PartialKey.Builder(DATASET, KIND1).build(); private static final PartialKey PARTIAL_KEY2 = new PartialKey.Builder(DATASET, KIND2).build(); - private static final Key KEY1 = PARTIAL_KEY1.toKey("name"); + private static final Key KEY1 = PARTIAL_KEY1.newKey("name"); private static final Key KEY2 = new Key.Builder(KEY1, KIND2, 1).build(); private static final Key KEY3 = KEY2.builder().name("bla").build(); private static final PartialEntity PARTIAL_ENTITY1 = new PartialEntity.Builder(PARTIAL_KEY2) @@ -74,14 +74,58 @@ public void testGetOptions() { } @Test - public void testNewTransaction() { + public void testNewTransactionCommit() { + fail("Not yet implemented"); + } + + @Test + public void testNewTransactionRollback() { fail("Not yet implemented"); } @Test public void testNewBatchWriter() { + BatchWriter batchWriter = datastore.newBatchWriter(); + Entity entity1 = ENTITY1.builder().clearProperties().build(); + Entity entity2 = ENTITY2.builder() + .clearProperties() + .setProperty("bla", new NullValue()) + .build(); + Entity entity4 = new Entity.Builder(KEY2.newKey("newName1")) + .setProperty("value", new StringValue("value")) + .build(); + Entity entity5 = new Entity.Builder(KEY2.newKey("newName2")) + .setProperty("value", new StringValue("value")) + .build(); + batchWriter.add(entity4, entity5); + batchWriter.put(ENTITY3, entity1, entity2); + batchWriter.submit(); + Iterator entities = datastore.get(KEY1, KEY2, KEY3, entity4.key(), entity5.key()); + assertEquals(entity1, entities.next()); + assertEquals(entity2, entities.next()); + assertEquals(ENTITY3, entities.next()); + assertEquals(entity4, entities.next()); + assertEquals(entity5, entities.next()); + assertFalse(entities.hasNext()); - fail("Not yet implemented"); + try { + batchWriter.submit(); + } catch (DatastoreServiceException ex) { + // expected to fail + } + batchWriter = datastore.newBatchWriter(); + batchWriter.delete(entity4.key(), entity5.key()); + batchWriter.update(ENTITY1, ENTITY2, ENTITY3); + batchWriter.submit(); + entities = datastore.get(KEY1, KEY2, KEY3, entity4.key(), entity5.key()); + assertEquals(ENTITY1, entities.next()); + assertEquals(ENTITY2, entities.next()); + assertEquals(ENTITY3, entities.next()); + assertNull(entities.next()); + assertNull(entities.next()); + assertFalse(entities.hasNext()); + + // TODO need to cover more edge cases (ds failures, re-use of same entities,..) } @Test @@ -100,15 +144,15 @@ public void testAllocateId() { assertEquals(key1.kind(), pk1.kind()); assertTrue(key1.hasId()); assertFalse(key1.hasName()); - assertEquals(pk1.toKey(key1.id()), key1); + assertEquals(pk1.newKey(key1.id()), key1); Key key2 = datastore.allocateId(pk1); assertNotEquals(key1, key2); - assertEquals(pk1.toKey(key2.id()), key2); + assertEquals(pk1.newKey(key2.id()), key2); Key key3 = datastore.allocateId(key1); assertNotEquals(key1, key3); - assertEquals(pk1.toKey(key3.id()), key3); + assertEquals(pk1.newKey(key3.id()), key3); } @Test @@ -125,9 +169,9 @@ public void testAllocateIdArray() { map.put(++count, result.next()); } assertEquals(6, map.size()); - assertEquals(pKey1.toKey(map.get(1).id()), map.get(1)); - assertEquals(pKey1.toKey(map.get(5).id()), map.get(5)); - assertEquals(pKey2.toKey(map.get(2).id()), map.get(2)); + assertEquals(pKey1.newKey(map.get(1).id()), map.get(1)); + assertEquals(pKey1.newKey(map.get(5).id()), map.get(5)); + assertEquals(pKey2.newKey(map.get(2).id()), map.get(2)); assertEquals(key3.builder().id(map.get(3).id()).build(), map.get(3)); assertEquals(key3.builder().id(map.get(6).id()).build(), map.get(6)); assertEquals(key4.builder().id(map.get(4).id()).build(), map.get(4)); From 0baefcf6f8fc5488ec25f7d0ef33d2a8245feff6 Mon Sep 17 00:00:00 2001 From: aozarov Date: Fri, 5 Dec 2014 12:45:59 -0800 Subject: [PATCH 032/771] Added some tests for batchwrite --- .../google/gcloud/datastore/PartialKey.java | 4 +- .../datastore/DatastoreServiceTest.java | 62 ++++++++++++++++--- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index 8f7f0b085739..d51cd19cd621 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -242,14 +242,14 @@ public Builder builder() { return new Builder(dataset(), kind()).namespace(namespace()).addAncestors(ancestors()); } - public Key toKey(String name) { + public Key newKey(String name) { return new Key.Builder(dataset(), kind(), name) .namespace(namespace()) .addAncestors(ancestors()) .build(); } - public Key toKey(long id) { + public Key newKey(long id) { return new Key.Builder(dataset(), kind(), id) .namespace(namespace()) .addAncestors(ancestors()) diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index 2c3914d2a4f6..4e9c23b421c1 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -26,7 +26,7 @@ public class DatastoreServiceTest { new BooleanValue.Builder(false).indexed(false).build(); private static final PartialKey PARTIAL_KEY1 = new PartialKey.Builder(DATASET, KIND1).build(); private static final PartialKey PARTIAL_KEY2 = new PartialKey.Builder(DATASET, KIND2).build(); - private static final Key KEY1 = PARTIAL_KEY1.toKey("name"); + private static final Key KEY1 = PARTIAL_KEY1.newKey("name"); private static final Key KEY2 = new Key.Builder(KEY1, KIND2, 1).build(); private static final Key KEY3 = KEY2.builder().name("bla").build(); private static final PartialEntity PARTIAL_ENTITY1 = new PartialEntity.Builder(PARTIAL_KEY2) @@ -74,14 +74,58 @@ public void testGetOptions() { } @Test - public void testNewTransaction() { + public void testNewTransactionCommit() { + fail("Not yet implemented"); + } + + @Test + public void testNewTransactionRollback() { fail("Not yet implemented"); } @Test public void testNewBatchWriter() { + BatchWriter batchWriter = datastore.newBatchWriter(); + Entity entity1 = ENTITY1.builder().clearProperties().build(); + Entity entity2 = ENTITY2.builder() + .clearProperties() + .setProperty("bla", new NullValue()) + .build(); + Entity entity4 = new Entity.Builder(KEY2.newKey("newName1")) + .setProperty("value", new StringValue("value")) + .build(); + Entity entity5 = new Entity.Builder(KEY2.newKey("newName2")) + .setProperty("value", new StringValue("value")) + .build(); + batchWriter.add(entity4, entity5); + batchWriter.put(ENTITY3, entity1, entity2); + batchWriter.submit(); + Iterator entities = datastore.get(KEY1, KEY2, KEY3, entity4.key(), entity5.key()); + assertEquals(entity1, entities.next()); + assertEquals(entity2, entities.next()); + assertEquals(ENTITY3, entities.next()); + assertEquals(entity4, entities.next()); + assertEquals(entity5, entities.next()); + assertFalse(entities.hasNext()); - fail("Not yet implemented"); + try { + batchWriter.submit(); + } catch (DatastoreServiceException ex) { + // expected to fail + } + batchWriter = datastore.newBatchWriter(); + batchWriter.delete(entity4.key(), entity5.key()); + batchWriter.update(ENTITY1, ENTITY2, ENTITY3); + batchWriter.submit(); + entities = datastore.get(KEY1, KEY2, KEY3, entity4.key(), entity5.key()); + assertEquals(ENTITY1, entities.next()); + assertEquals(ENTITY2, entities.next()); + assertEquals(ENTITY3, entities.next()); + assertNull(entities.next()); + assertNull(entities.next()); + assertFalse(entities.hasNext()); + + // TODO need to cover more edge cases (ds failures, re-use of same entities,..) } @Test @@ -100,15 +144,15 @@ public void testAllocateId() { assertEquals(key1.kind(), pk1.kind()); assertTrue(key1.hasId()); assertFalse(key1.hasName()); - assertEquals(pk1.toKey(key1.id()), key1); + assertEquals(pk1.newKey(key1.id()), key1); Key key2 = datastore.allocateId(pk1); assertNotEquals(key1, key2); - assertEquals(pk1.toKey(key2.id()), key2); + assertEquals(pk1.newKey(key2.id()), key2); Key key3 = datastore.allocateId(key1); assertNotEquals(key1, key3); - assertEquals(pk1.toKey(key3.id()), key3); + assertEquals(pk1.newKey(key3.id()), key3); } @Test @@ -125,9 +169,9 @@ public void testAllocateIdArray() { map.put(++count, result.next()); } assertEquals(6, map.size()); - assertEquals(pKey1.toKey(map.get(1).id()), map.get(1)); - assertEquals(pKey1.toKey(map.get(5).id()), map.get(5)); - assertEquals(pKey2.toKey(map.get(2).id()), map.get(2)); + assertEquals(pKey1.newKey(map.get(1).id()), map.get(1)); + assertEquals(pKey1.newKey(map.get(5).id()), map.get(5)); + assertEquals(pKey2.newKey(map.get(2).id()), map.get(2)); assertEquals(key3.builder().id(map.get(3).id()).build(), map.get(3)); assertEquals(key3.builder().id(map.get(6).id()).build(), map.get(6)); assertEquals(key4.builder().id(map.get(4).id()).build(), map.get(4)); From db7b76b2829ae2392dd3336109c9f55b0a1c7a04 Mon Sep 17 00:00:00 2001 From: aozarov Date: Sat, 6 Dec 2014 18:22:54 -0800 Subject: [PATCH 033/771] Adding PropertyContainer as a convinient way to add Properties --- .../gcloud/datastore/PropertyContainer.java | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 src/main/java/com/google/gcloud/datastore/PropertyContainer.java diff --git a/src/main/java/com/google/gcloud/datastore/PropertyContainer.java b/src/main/java/com/google/gcloud/datastore/PropertyContainer.java new file mode 100644 index 000000000000..a63026411d82 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/PropertyContainer.java @@ -0,0 +1,144 @@ +package com.google.gcloud.datastore; + +import com.google.api.services.datastore.DatastoreV1; +import com.google.common.collect.ImmutableSortedMap; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * A container of properties (name and Value pairs). + */ +abstract class PropertyContainer + , B extends PropertyContainer.Builder> + extends Serializable { + + private static final long serialVersionUID = 8175618724683792766L; + + private final transient ImmutableSortedMap> properties; + + protected abstract static class Builder + , B extends Builder> { + + private final Map> properties; + + public Builder() { + properties = new HashMap<>(); + } + + public Builder(PropertyContainer entity) { + properties = new HashMap<>(entity.properties()); + } + + protected B self() { + return (B) this; + } + + public B clearProperties() { + properties.clear(); + return self(); + } + + public B removeProperty(String name) { + properties.remove(name); + return self(); + } + + public B setProperty(String name, Value value) { + properties.put(name, value); + return self(); + } + + setNullProperty(String name); + longValue + booleanValue + doubleValue + dateTimeValue + PartialEntityValue + KeyValue + ListValue + blobValue + rawValue // should work for all.... + + + + public E build() { + return build(ImmutableSortedMap.copyOf(properties)); + } + + protected abstract E build(ImmutableSortedMap> properties); + } + + protected PropertyContainer(ImmutableSortedMap> properties) { + this.properties = properties; + } + + public boolean hasProperty(String name) { + return properties.containsKey(name); + } + + @SuppressWarnings("unchecked") + public > V property(String name) { + return (V) properties.get(name); + } + + public boolean isNull(String name) { + return properties.get(name) instanceof NullValue; + } + + public String stringProperty(String name) { + return ((StringValue) property(name)).get(); + } + + longProperty + booleanValue + doubleValue + dateTimeValue + PartialEntityValue + KeyValue + ListValue + blobValue + rawValue // should work for all.... + + public Set propertyNames() { + return properties.keySet(); + } + + /** + * Returns a new builder for this entity (values are copied). + */ + public abstract B builder(); + + ImmutableSortedMap> properties() { + return properties; + } + + @Override + public int hashCode() { + return properties.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PropertyContainer)) { + return false; + } + return properties.equals(((PropertyContainer) obj).properties); + } + + @Override + protected final DatastoreV1.Entity toPb() { + DatastoreV1.Entity.Builder entityPb = DatastoreV1.Entity.newBuilder(); + for (Map.Entry> entry : properties.entrySet()) { + DatastoreV1.Property.Builder propertyPb = DatastoreV1.Property.newBuilder(); + propertyPb.setName(entry.getKey()); + propertyPb.setValue(entry.getValue().toPb()); + entityPb.addProperty(propertyPb.build()); + } + populateEntityBuilder(entityPb); + return entityPb.build(); + } + + protected abstract void populateEntityBuilder(DatastoreV1.Entity.Builder entity); +} From 31cf798de84c4214264ea1cb466e2590bf8c39e7 Mon Sep 17 00:00:00 2001 From: aozarov Date: Sun, 7 Dec 2014 13:09:17 -0800 Subject: [PATCH 034/771] add direct value set/get to Value --- .../gcloud/datastore/PropertyContainer.java | 145 +++++++++++++++--- .../datastore/DatastoreServiceTest.java | 1 + 2 files changed, 123 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/PropertyContainer.java b/src/main/java/com/google/gcloud/datastore/PropertyContainer.java index a63026411d82..8016561ef5a6 100644 --- a/src/main/java/com/google/gcloud/datastore/PropertyContainer.java +++ b/src/main/java/com/google/gcloud/datastore/PropertyContainer.java @@ -2,11 +2,18 @@ import com.google.api.services.datastore.DatastoreV1; import com.google.common.collect.ImmutableSortedMap; +import com.google.gcloud.datastore.Value.Type; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; + +// TODO: make entity and Partial entity extends it and to tests + documentaion the option +// of list and direct value set/get. + /** * A container of properties (name and Value pairs). */ @@ -31,6 +38,7 @@ public Builder(PropertyContainer entity) { properties = new HashMap<>(entity.properties()); } + @SuppressWarnings("unchecked") protected B self() { return (B) this; } @@ -50,18 +58,60 @@ public B setProperty(String name, Value value) { return self(); } - setNullProperty(String name); - longValue - booleanValue - doubleValue - dateTimeValue - PartialEntityValue - KeyValue - ListValue - blobValue - rawValue // should work for all.... + public B setNullProperty(String name) { + properties.put(name, new NullValue()); + return self(); + } + + public B setStringProperty(String name, String value) { + properties.put(name, new StringValue(value)); + return self(); + } + + public B setLongProperty(String name, long value) { + properties.put(name, new LongValue(value)); + return self(); + } + + public B setDoubleProperty(String name, double value) { + properties.put(name, new DoubleValue(value)); + return self(); + } + + public B setBooleanProperty(String name, boolean value) { + properties.put(name, new BooleanValue(value)); + return self(); + } + + public B setDateAndTimeProperty(String name, DateAndTime value) { + properties.put(name, new DateAndTimeValue(value)); + return self(); + } + public B setKeyProperty(String name, Key value) { + properties.put(name, new KeyValue(value)); + return self(); + } + public B setPartialEntityProperty(String name, PartialEntity value) { + properties.put(name, new PartialEntityValue(value)); + return self(); + } + + public B setListProperty(String name, List> values) { + properties.put(name, new ListValue(values)); + return self(); + } + + public B setListProperty(String name, Value... value) { + properties.put(name, new ListValue(Arrays.asList(value))); + return self(); + } + + public B setBlobProperty(String name, Blob value) { + properties.put(name, new BlobValue(value)); + return self(); + } public E build() { return build(ImmutableSortedMap.copyOf(properties)); @@ -74,32 +124,81 @@ protected PropertyContainer(ImmutableSortedMap> propertie this.properties = properties; } + /** + * Returns {@code true} if there is such property with the given {@code name}. + */ public boolean hasProperty(String name) { return properties.containsKey(name); } - @SuppressWarnings("unchecked") + /** + * Returns the {@link Value} of property with the given {@code name}. + * + * @throws DatastoreServiceException if not such property. + */ public > V property(String name) { - return (V) properties.get(name); + @SuppressWarnings("unchecked") + V property = (V) properties.get(name); + if (property == null) { + throw DatastoreServiceException.throwInvalidRequest("No such property %s", name); + } + return property; } - public boolean isNull(String name) { - return properties.get(name) instanceof NullValue; + public Type propertyType(String name) { + return property(name).type(); + } + + public boolean isNullProperty(String name) { + return property(name) instanceof NullValue; } public String stringProperty(String name) { return ((StringValue) property(name)).get(); } - longProperty - booleanValue - doubleValue - dateTimeValue - PartialEntityValue - KeyValue - ListValue - blobValue - rawValue // should work for all.... + public long longProperty(String name) { + return ((LongValue) property(name)).get(); + } + + public double doubleProperty(String name) { + return ((DoubleValue) property(name)).get(); + } + + public boolean booleanProperty(String name) { + return ((BooleanValue) property(name)).get(); + } + + public DateAndTime dateAndTimeProperty(String name) { + return ((DateAndTimeValue) property(name)).get(); + } + + public Key keyProperty(String name) { + return ((KeyValue) property(name)).get(); + } + + public PartialEntity partialEntityProperty(String name) { + return ((PartialEntityValue) property(name)).get(); + } + + public List> listProperty(String name) { + return ((ListValue) property(name)).get(); + } + + public Blob blobProperty(String name) { + return ((BlobValue) property(name)).get(); + } + + /** + * Returns the property's value as {@code RawValue}. + */ + public RawValue rawValueProperty(String name) { + Value value = property(name); + if (value instanceof RawValue) { + return (RawValue) value; + } + return new RawValue(value.toPb()); + } public Set propertyNames() { return properties.keySet(); diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index 4e9c23b421c1..1b5d9f9869d9 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -75,6 +75,7 @@ public void testGetOptions() { @Test public void testNewTransactionCommit() { + // TODO (also try list value with different types) fail("Not yet implemented"); } From 5a2398b0e7519a38375d8fefd4171498167a741d Mon Sep 17 00:00:00 2001 From: ozarov Date: Sun, 7 Dec 2014 22:59:53 -0800 Subject: [PATCH 035/771] work in progress --- .classpath | 10 ---------- .../com/google/gcloud/datastore/DateAndTime.java | 3 +-- .../com/google/gcloud/datastore/ListValue.java | 16 ++++++++-------- .../gcloud/datastore/DatastoreServiceTest.java | 9 +++++++++ 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/.classpath b/.classpath index d6cf6121af66..9ed6ee4d0713 100644 --- a/.classpath +++ b/.classpath @@ -23,15 +23,5 @@
- - - - - - - - - -
diff --git a/src/main/java/com/google/gcloud/datastore/DateAndTime.java b/src/main/java/com/google/gcloud/datastore/DateAndTime.java index 492802e3b8dc..38124f0526da 100644 --- a/src/main/java/com/google/gcloud/datastore/DateAndTime.java +++ b/src/main/java/com/google/gcloud/datastore/DateAndTime.java @@ -8,8 +8,7 @@ import java.util.Date; /** - * A Google Cloud Datastore timestamp. - * A Datastore timestamp is represented in micro-seconds. + * A Google Cloud Datastore timestamp (represented in micro-seconds). * This class is immutable. * * @see Google Cloud Datastore Entities, Properties, and Keys diff --git a/src/main/java/com/google/gcloud/datastore/ListValue.java b/src/main/java/com/google/gcloud/datastore/ListValue.java index b8bdb7ec17d6..ef5c64a02e4f 100644 --- a/src/main/java/com/google/gcloud/datastore/ListValue.java +++ b/src/main/java/com/google/gcloud/datastore/ListValue.java @@ -10,12 +10,12 @@ import java.util.List; public final class ListValue extends - Value>, ListValue, ListValue.Builder> { + Value>, ListValue, ListValue.Builder> { private static final long serialVersionUID = -5461475706792576395L; - static final BaseMarshaller>, ListValue, Builder> MARSHALLER = - new BaseMarshaller>, ListValue, Builder>() { + static final BaseMarshaller>, ListValue, Builder> MARSHALLER = + new BaseMarshaller>, ListValue, Builder>() { @Override public int getProtoFieldId() { @@ -23,7 +23,7 @@ public int getProtoFieldId() { } @Override - public Builder newBuilder(List> values) { + public Builder newBuilder(List> values) { return new Builder().set(values); } @@ -45,7 +45,7 @@ protected void setValue(ListValue from, DatastoreV1.Value.Builder to) { }; public static final class Builder extends - Value.BaseBuilder>, ListValue, Builder> { + Value.BaseBuilder>, ListValue, Builder> { private ImmutableList.Builder> listBuilder = ImmutableList.builder(); @@ -74,7 +74,7 @@ public Builder addValue(Value first, Value... other) { * @see com.google.gcloud.datastore.Value.BaseBuilder#set(java.lang.Object) */ @Override - public Builder set(List> properties) { + public Builder set(List> properties) { listBuilder = ImmutableList.>builder(); for (Value property : properties) { addValue(property); @@ -83,7 +83,7 @@ public Builder set(List> properties) { } @Override - public List> get() { + public List> get() { return listBuilder.build(); } @@ -94,7 +94,7 @@ public ListValue build() { } } - public ListValue(List> properties) { + public ListValue(List> properties) { this(new Builder().set(properties)); } diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index 1b5d9f9869d9..2ea5a57e9e2c 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -11,6 +11,7 @@ import org.junit.Before; import org.junit.Test; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -29,14 +30,22 @@ public class DatastoreServiceTest { private static final Key KEY1 = PARTIAL_KEY1.newKey("name"); private static final Key KEY2 = new Key.Builder(KEY1, KIND2, 1).build(); private static final Key KEY3 = KEY2.builder().name("bla").build(); + private static final KeyValue KEY_VALUE = new KeyValue(KEY1); + private static final ListValue LIST_VALUE1 = new ListValue.Builder() + .addValue(NULL_VALUE) + .addValue(STR_VALUE, BOOL_VALUE) + .build(); + private static final ListValue LIST_VALUE2 = new ListValue(Collections.singletonList(KEY_VALUE)); private static final PartialEntity PARTIAL_ENTITY1 = new PartialEntity.Builder(PARTIAL_KEY2) .setProperty("str", STR_VALUE) .setProperty("bool", BOOL_VALUE) + .setProperty("list", LIST_VALUE1) .build(); private static final Entity ENTITY1 = new Entity.Builder(KEY1) .setProperty("str", STR_VALUE) .setProperty("bool", BOOL_VALUE) .setProperty("partial1", new PartialEntityValue(PARTIAL_ENTITY1)) + .setProperty("list", LIST_VALUE2) .build(); private static final Entity ENTITY2 = new Entity.Builder(KEY2, ENTITY1) .removeProperty("str") From f6d541a3802de5ffe3aa4a56e30d3de8ed799476 Mon Sep 17 00:00:00 2001 From: ozarov Date: Sun, 7 Dec 2014 23:49:51 -0800 Subject: [PATCH 036/771] work in progress --- .../gcloud/datastore/DateAndTimeValue.java | 57 ------------- .../{DateAndTime.java => DateTime.java} | 18 ++--- .../gcloud/datastore/DateTimeValue.java | 57 +++++++++++++ .../com/google/gcloud/datastore/Entity.java | 41 ++++------ .../gcloud/datastore/PartialEntity.java | 80 +++++-------------- .../gcloud/datastore/PropertyContainer.java | 30 +++---- .../com/google/gcloud/datastore/Value.java | 4 +- .../google/gcloud/datastore/package-info.java | 15 ++-- .../datastore/DatastoreServiceTest.java | 12 +-- .../gcloud/datastore/SerializationTest.java | 6 +- 10 files changed, 127 insertions(+), 193 deletions(-) delete mode 100644 src/main/java/com/google/gcloud/datastore/DateAndTimeValue.java rename src/main/java/com/google/gcloud/datastore/{DateAndTime.java => DateTime.java} (72%) create mode 100644 src/main/java/com/google/gcloud/datastore/DateTimeValue.java diff --git a/src/main/java/com/google/gcloud/datastore/DateAndTimeValue.java b/src/main/java/com/google/gcloud/datastore/DateAndTimeValue.java deleted file mode 100644 index e1fa416c6bb1..000000000000 --- a/src/main/java/com/google/gcloud/datastore/DateAndTimeValue.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.google.gcloud.datastore; - -import static com.google.api.services.datastore.DatastoreV1.Value.TIMESTAMP_MICROSECONDS_VALUE_FIELD_NUMBER; - -import com.google.api.services.datastore.DatastoreV1; - -public final class DateAndTimeValue - extends Value { - - private static final long serialVersionUID = -5096238337676649540L; - - static final BaseMarshaller MARSHALLER = - new BaseMarshaller() { - - @Override - public int getProtoFieldId() { - return TIMESTAMP_MICROSECONDS_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(DateAndTime value) { - return new Builder(value); - } - - @Override - protected DateAndTime getValue(DatastoreV1.Value from) { - return new DateAndTime(from.getTimestampMicrosecondsValue()); - } - - @Override - protected void setValue(DateAndTimeValue from, DatastoreV1.Value.Builder to) { - to.setTimestampMicrosecondsValue(from.get().timestampMicroseconds()); - } - }; - - public static final class Builder - extends Value.BaseBuilder { - - public Builder(DateAndTime dateAndTime) { - super(Type.DATE_AND_TIME); - set(dateAndTime); - } - - @Override - public DateAndTimeValue build() { - return new DateAndTimeValue(this); - } - } - - public DateAndTimeValue(DateAndTime dateAndTime) { - this(new Builder(dateAndTime)); - } - - DateAndTimeValue(Builder builder) { - super(builder); - } -} diff --git a/src/main/java/com/google/gcloud/datastore/DateAndTime.java b/src/main/java/com/google/gcloud/datastore/DateTime.java similarity index 72% rename from src/main/java/com/google/gcloud/datastore/DateAndTime.java rename to src/main/java/com/google/gcloud/datastore/DateTime.java index 38124f0526da..7ade5c46d229 100644 --- a/src/main/java/com/google/gcloud/datastore/DateAndTime.java +++ b/src/main/java/com/google/gcloud/datastore/DateTime.java @@ -13,13 +13,13 @@ * * @see Google Cloud Datastore Entities, Properties, and Keys */ -public final class DateAndTime implements java.io.Serializable { +public final class DateTime implements java.io.Serializable { private static final long serialVersionUID = 7343324797621228378L; private final long timestampMicroseconds; - DateAndTime(long timestampMicroseconds) { + DateTime(long timestampMicroseconds) { this.timestampMicroseconds = timestampMicroseconds; } @@ -35,10 +35,10 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (!(obj instanceof DateAndTime)) { + if (!(obj instanceof DateTime)) { return false; } - return timestampMicroseconds == ((DateAndTime) obj).timestampMicroseconds; + return timestampMicroseconds == ((DateTime) obj).timestampMicroseconds; } public long timestampMicroseconds() { @@ -59,15 +59,15 @@ public Calendar toCalendar() { return cal; } - public static DateAndTime now() { - return new DateAndTime(System.nanoTime() / 1000L); + public static DateTime now() { + return new DateTime(System.nanoTime() / 1000L); } - public static DateAndTime copyFrom(Date date) { - return new DateAndTime(checkNotNull(date).getTime() * 1000L); + public static DateTime copyFrom(Date date) { + return new DateTime(checkNotNull(date).getTime() * 1000L); } - public static DateAndTime copyFrom(Calendar calendar) { + public static DateTime copyFrom(Calendar calendar) { return copyFrom(calendar.getTime()); } } diff --git a/src/main/java/com/google/gcloud/datastore/DateTimeValue.java b/src/main/java/com/google/gcloud/datastore/DateTimeValue.java new file mode 100644 index 000000000000..fc2258c26c10 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DateTimeValue.java @@ -0,0 +1,57 @@ +package com.google.gcloud.datastore; + +import static com.google.api.services.datastore.DatastoreV1.Value.TIMESTAMP_MICROSECONDS_VALUE_FIELD_NUMBER; + +import com.google.api.services.datastore.DatastoreV1; + +public final class DateTimeValue + extends Value { + + private static final long serialVersionUID = -5096238337676649540L; + + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { + + @Override + public int getProtoFieldId() { + return TIMESTAMP_MICROSECONDS_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(DateTime value) { + return new Builder(value); + } + + @Override + protected DateTime getValue(DatastoreV1.Value from) { + return new DateTime(from.getTimestampMicrosecondsValue()); + } + + @Override + protected void setValue(DateTimeValue from, DatastoreV1.Value.Builder to) { + to.setTimestampMicrosecondsValue(from.get().timestampMicroseconds()); + } + }; + + public static final class Builder + extends Value.BaseBuilder { + + public Builder(DateTime dateTime) { + super(Type.DATE_TIME); + set(dateTime); + } + + @Override + public DateTimeValue build() { + return new DateTimeValue(this); + } + } + + public DateTimeValue(DateTime dateTime) { + this(new Builder(dateTime)); + } + + DateTimeValue(Builder builder) { + super(builder); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/Entity.java b/src/main/java/com/google/gcloud/datastore/Entity.java index 979174942e06..d6d671439d6f 100644 --- a/src/main/java/com/google/gcloud/datastore/Entity.java +++ b/src/main/java/com/google/gcloud/datastore/Entity.java @@ -1,5 +1,7 @@ package com.google.gcloud.datastore; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSortedMap; @@ -10,7 +12,7 @@ * An entity holds one or more properties, represented by a name (as {@link String}) * and a value (as {@link Value}), and is associated with a {@link Key}. * For a list of possible values see {@link Value.Type}. - * This class is immutable. To edit (a copy) use {@link #builder()}. + * This class is immutable. * * @see Google Cloud Datastore Entities, Properties, and Keys */ @@ -18,45 +20,35 @@ public final class Entity extends PartialEntity { private static final long serialVersionUID = 432961565733066915L; - public static final class Builder extends PartialEntity.Builder { + public static final class Builder extends PropertyContainer.Builder { + + private Key key; public Builder(Key key) { - super(key); + this.key = checkNotNull(key); } public Builder(Entity entity) { super(entity); + key = entity.key(); } /** * Create a Builder for the given key and with the properties from the given entity. */ public Builder(Key key, PartialEntity entity) { - super(key, entity); - } - - @Override - public Builder clearProperties() { - super.clearProperties(); - return this; - } - - @Override - public Builder removeProperty(String name) { - super.removeProperty(name); - return this; + super(entity); + this.key = key; } - @Override - public Builder setProperty(String name, Value value) { - super.setProperty(name, value); + public Builder key(Key key) { + this.key = checkNotNull(key); return this; } @Override - public Entity build() { - PartialEntity entity = super.build(); - return new Entity((Key) entity.key(), entity.properties()); + protected Entity build(ImmutableSortedMap> properties) { + return new Entity(key, properties); } } @@ -72,11 +64,6 @@ public Key key() { return (Key) super.key(); } - @Override - public Builder builder() { - return new Builder(this); - } - @Override protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { return fromPb(DatastoreV1.Entity.parseFrom(bytesPb)); diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java index 7d22be430372..8e1e8578e291 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java @@ -4,71 +4,59 @@ import com.google.common.collect.ImmutableSortedMap; import com.google.protobuf.InvalidProtocolBufferException; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; -import java.util.Set; /** * A partial entity holds one or more properties, represented by a name (as {@link String}) * and a value (as {@link Value}). * For a list of possible values see {@link Value.Type}. * A partial entity also can be associated with a key (partial or full). - * This class is immutable. To edit (a copy) use {@link #builder()}. + * This class is immutable. */ -public class PartialEntity extends Serializable { +public class PartialEntity extends PropertyContainer { private static final long serialVersionUID = 6492561268709192891L; private final transient PartialKey key; - private final transient ImmutableSortedMap> properties; - public static class Builder { + public static class Builder extends PropertyContainer.Builder { - private final PartialKey key; - private final Map> properties; + private PartialKey key; + + public Builder() { + } /** * Construct a builder with a partial key (could be null). */ public Builder(PartialKey key) { this.key = key; - properties = new HashMap<>(); } public Builder(PartialEntity entity) { + super(entity); key = entity.key(); - properties = new HashMap<>(entity.properties()); } public Builder(PartialKey key, PartialEntity entity) { + super(entity); this.key = key; - properties = new HashMap<>(entity.properties()); - } - - public Builder clearProperties() { - properties.clear(); - return this; } - public Builder removeProperty(String name) { - properties.remove(name); - return this; - } - - public Builder setProperty(String name, Value value) { - properties.put(name, value); + public Builder key(PartialKey key) { + this.key = key; return this; } - public PartialEntity build() { - return new PartialEntity(key, ImmutableSortedMap.copyOf(properties)); + @Override + protected PartialEntity build(ImmutableSortedMap> properties) { + return new PartialEntity(key, properties); } } protected PartialEntity(PartialKey key, ImmutableSortedMap> properties) { + super(properties); this.key = key; - this.properties = properties; } /** @@ -78,33 +66,9 @@ public PartialKey key() { return key; } - public boolean hasProperty(String name) { - return properties.containsKey(name); - } - - @SuppressWarnings("unchecked") - public > V property(String name) { - return (V) properties.get(name); - } - - public Set propertyNames() { - return properties.keySet(); - } - - /** - * Returns a new builder for this entity (values are copied). - */ - public Builder builder() { - return new Builder(this); - } - - ImmutableSortedMap> properties() { - return properties; - } - @Override public int hashCode() { - return Objects.hash(key, properties); + return Objects.hash(key, properties()); } @Override @@ -114,22 +78,14 @@ public boolean equals(Object obj) { } PartialEntity other = (PartialEntity) obj; return Objects.equals(key, other.key) - && Objects.equals(properties, other.properties); + && super.equals(obj); } @Override - protected DatastoreV1.Entity toPb() { - DatastoreV1.Entity.Builder entityPb = DatastoreV1.Entity.newBuilder(); + protected void populateEntityBuilder(DatastoreV1.Entity.Builder entityPb) { if (key != null) { entityPb.setKey(key.toPb()); } - for (Map.Entry> entry : properties.entrySet()) { - DatastoreV1.Property.Builder propertyPb = DatastoreV1.Property.newBuilder(); - propertyPb.setName(entry.getKey()); - propertyPb.setValue(entry.getValue().toPb()); - entityPb.addProperty(propertyPb.build()); - } - return entityPb.build(); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/PropertyContainer.java b/src/main/java/com/google/gcloud/datastore/PropertyContainer.java index 8016561ef5a6..c2393ba2a741 100644 --- a/src/main/java/com/google/gcloud/datastore/PropertyContainer.java +++ b/src/main/java/com/google/gcloud/datastore/PropertyContainer.java @@ -10,23 +10,16 @@ import java.util.Map; import java.util.Set; - -// TODO: make entity and Partial entity extends it and to tests + documentaion the option -// of list and direct value set/get. - /** * A container of properties (name and Value pairs). */ -abstract class PropertyContainer - , B extends PropertyContainer.Builder> - extends Serializable { +abstract class PropertyContainer extends Serializable { private static final long serialVersionUID = 8175618724683792766L; private final transient ImmutableSortedMap> properties; - protected abstract static class Builder - , B extends Builder> { + protected abstract static class Builder> { private final Map> properties; @@ -34,7 +27,7 @@ public Builder() { properties = new HashMap<>(); } - public Builder(PropertyContainer entity) { + public Builder(PropertyContainer entity) { properties = new HashMap<>(entity.properties()); } @@ -83,8 +76,8 @@ public B setBooleanProperty(String name, boolean value) { return self(); } - public B setDateAndTimeProperty(String name, DateAndTime value) { - properties.put(name, new DateAndTimeValue(value)); + public B setDateAndTimeProperty(String name, DateTime value) { + properties.put(name, new DateTimeValue(value)); return self(); } @@ -169,8 +162,8 @@ public boolean booleanProperty(String name) { return ((BooleanValue) property(name)).get(); } - public DateAndTime dateAndTimeProperty(String name) { - return ((DateAndTimeValue) property(name)).get(); + public DateTime dateAndTimeProperty(String name) { + return ((DateTimeValue) property(name)).get(); } public Key keyProperty(String name) { @@ -204,11 +197,6 @@ public Set propertyNames() { return properties.keySet(); } - /** - * Returns a new builder for this entity (values are copied). - */ - public abstract B builder(); - ImmutableSortedMap> properties() { return properties; } @@ -223,7 +211,7 @@ public boolean equals(Object obj) { if (!(obj instanceof PropertyContainer)) { return false; } - return properties.equals(((PropertyContainer) obj).properties); + return properties.equals(((PropertyContainer) obj).properties); } @Override @@ -239,5 +227,5 @@ protected final DatastoreV1.Entity toPb() { return entityPb.build(); } - protected abstract void populateEntityBuilder(DatastoreV1.Entity.Builder entity); + protected abstract void populateEntityBuilder(DatastoreV1.Entity.Builder entityPb); } diff --git a/src/main/java/com/google/gcloud/datastore/Value.java b/src/main/java/com/google/gcloud/datastore/Value.java index 3d24423b65d8..03b40e61d98e 100644 --- a/src/main/java/com/google/gcloud/datastore/Value.java +++ b/src/main/java/com/google/gcloud/datastore/Value.java @@ -83,9 +83,9 @@ public enum Type { BOOLEAN(BooleanValue.MARSHALLER, BooleanValue.MARSHALLER), /** - * Represents a {@link DateAndTime} value. + * Represents a {@link DateTime} value. */ - DATE_AND_TIME(DateAndTimeValue.MARSHALLER, DateAndTimeValue.MARSHALLER), + DATE_TIME(DateTimeValue.MARSHALLER, DateTimeValue.MARSHALLER), /** * Represents a {@link Blob} value. diff --git a/src/main/java/com/google/gcloud/datastore/package-info.java b/src/main/java/com/google/gcloud/datastore/package-info.java index a5ec1bad4c05..23b61cade70c 100644 --- a/src/main/java/com/google/gcloud/datastore/package-info.java +++ b/src/main/java/com/google/gcloud/datastore/package-info.java @@ -9,18 +9,21 @@ * Entity entity = datastore.get(key); * if (entity == null) { * entity = new Entity.Builder(key) - * .setProperty("name", new StringValue("John Do")) + * .setStringProperty("name", "John Do") * .setProperty("age", new LongValue.Builder(100).indexed(false).build()) - * .setProperty("updated", new BooleanValue(false)) + * .setBooleanProperty("updated", false) * .build(); * datastore.put(entity); * } else { - * BooleanValue updated = entity.property("updated"); - * if (!updated.get()) { + * boolean updated = entity.booleanProperty("updated"); + * if (!updated.get) { + * String[] name = entity.stringProperty("name").split(" "); * entity = entity.builder() - * .setProperty("updated", new BooleanValue(true)) + * .setStringProperty("name", name[0]) + * .setProperty("last_name", new StringProperty.Builder(name[1]).indexed(false).build()) + * .setBooleanProperty("updated", true) * .removeProperty("old_property") - * .setProperty("new_property", new DoubleValue(1.1)) + * .setDoubleProperty("new_property", 1.1) * .build(); * datastore.update(entity); * } diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index 2ea5a57e9e2c..3cc103450e0c 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -96,16 +96,16 @@ public void testNewTransactionRollback() { @Test public void testNewBatchWriter() { BatchWriter batchWriter = datastore.newBatchWriter(); - Entity entity1 = ENTITY1.builder().clearProperties().build(); - Entity entity2 = ENTITY2.builder() + Entity entity1 = new Entity.Builder(ENTITY1).clearProperties().build(); + Entity entity2 = new Entity.Builder(ENTITY2) .clearProperties() - .setProperty("bla", new NullValue()) + .setNullProperty("bla") .build(); Entity entity4 = new Entity.Builder(KEY2.newKey("newName1")) .setProperty("value", new StringValue("value")) .build(); Entity entity5 = new Entity.Builder(KEY2.newKey("newName2")) - .setProperty("value", new StringValue("value")) + .setStringProperty("value", "value") .build(); batchWriter.add(entity4, entity5); batchWriter.put(ENTITY3, entity1, entity2); @@ -244,7 +244,7 @@ public void testUpdate() { } datastore.add(ENTITY3); assertEquals(ENTITY3, datastore.get(ENTITY3.key())); - Entity entity3 = ENTITY3.builder() + Entity entity3 = new Entity.Builder(ENTITY3) .clearProperties() .setProperty("bla", new NullValue()) .build(); @@ -261,7 +261,7 @@ public void testPut() { assertNull(keys.next()); assertFalse(keys.hasNext()); - Entity entity2 = ENTITY2.builder() + Entity entity2 = new Entity.Builder(ENTITY2) .clearProperties() .setProperty("bla", new NullValue()) .build(); diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index d450d84f08b8..58a2b14d4d1c 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -30,8 +30,8 @@ public class SerializationTest { private static final LongValue LONG_VALUE = new LongValue(123); private static final DoubleValue DOUBLE_VALUE = new DoubleValue(12.34); private static final BooleanValue BOOLEAN_VALUE = new BooleanValue(true); - private static final DateAndTimeValue DATE_AND_TIME_VALUE = - new DateAndTimeValue(DateAndTime.now()); + private static final DateTimeValue DATE_AND_TIME_VALUE = + new DateTimeValue(DateTime.now()); private static final BlobValue BLOB_VALUE = new BlobValue(Blob.copyFrom(new byte[] {10, 0, -2})); private static final RawValue RAW_VALUE = new RawValue( @@ -76,7 +76,7 @@ public class SerializationTest { .put(Type.LONG, LONG_VALUE) .put(Type.DOUBLE, DOUBLE_VALUE) .put(Type.BOOLEAN, BOOLEAN_VALUE) - .put(Type.DATE_AND_TIME, DATE_AND_TIME_VALUE) + .put(Type.DATE_TIME, DATE_AND_TIME_VALUE) .put(Type.BLOB, BLOB_VALUE) .put(Type.RAW_VALUE, RAW_VALUE) .build(); From e9f8f4918cfa7ff3ab7800a9fee47c9397d697c0 Mon Sep 17 00:00:00 2001 From: aozarov Date: Mon, 8 Dec 2014 09:00:31 -0800 Subject: [PATCH 037/771] remove Key's builder --- .../java/com/google/gcloud/datastore/Key.java | 18 ++++++++++-------- .../google/gcloud/datastore/PartialKey.java | 13 ++++++++----- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index c490dded05f5..14c27114a589 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -14,7 +14,7 @@ /** * A key that is guaranteed to be complete and could be used to reference a * Google Cloud Datastore {@link Entity}. - * This class is immutable. To edit (a copy) use {@link #builder()}. + * This class is immutable. * * @see Google Cloud Datastore Entities, Properties, and Keys */ @@ -47,6 +47,15 @@ public Builder(Key parent, String kind, long id) { this.id = id; } + public Builder(Key from) { + super(from); + if (from.hasId()) { + id = from.id(); + } else { + name = from.name(); + } + } + @Override public Builder addAncestor(String kind, long id) { super.addAncestor(kind, id); @@ -122,13 +131,6 @@ private Key(PartialKey key, long id) { super(key.dataset(), key.namespace(), newPath(key, id)); } - @Override - public Builder builder() { - Builder builder = - hasId() ? new Builder(dataset(), kind(), id()) : new Builder(dataset(), kind(), name()); - return builder.namespace(namespace()).addAncestors(ancestors()); - } - public boolean hasId() { return id() != null; } diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index d51cd19cd621..51ece279e54c 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -18,7 +18,7 @@ /** * A partial key (without a name or id). * Could be used as metadata for {@link PartialEntity}. - * This class is immutable. To edit (a copy) use {@link #builder()}. + * This class is immutable. */ public class PartialKey extends Serializable { @@ -142,6 +142,13 @@ public Builder(Key parent, String kind) { this.kind = kind; } + public Builder(PartialKey from) { + dataset = from.dataset(); + namespace = from.namespace(); + kind = from.kind(); + ancestors.addAll(from.ancestors()); + } + public Builder addAncestor(String kind, long id) { checkArgument(id != 0, "id must not be equal to zero"); return addAncestor(new Ancestor(kind, id)); @@ -238,10 +245,6 @@ public String kind() { return getLeaf().kind(); } - public Builder builder() { - return new Builder(dataset(), kind()).namespace(namespace()).addAncestors(ancestors()); - } - public Key newKey(String name) { return new Key.Builder(dataset(), kind(), name) .namespace(namespace()) From d67e7e35139c7ca5a67d7b8e0d4e3a4defa7deb1 Mon Sep 17 00:00:00 2001 From: aozarov Date: Mon, 8 Dec 2014 17:39:06 -0800 Subject: [PATCH 038/771] Change Builders to factory style --- .../com/google/gcloud/ServiceOptions.java | 2 +- ...PropertyContainer.java => BaseEntity.java} | 18 +- .../com/google/gcloud/datastore/BaseKey.java | 193 +++++++++++ .../google/gcloud/datastore/BlobValue.java | 13 +- .../google/gcloud/datastore/BooleanValue.java | 13 +- .../datastore/DatastoreServiceImpl.java | 9 +- .../datastore/DatastoreServiceOptions.java | 13 +- .../gcloud/datastore/DateTimeValue.java | 13 +- .../google/gcloud/datastore/DoubleValue.java | 13 +- .../com/google/gcloud/datastore/Entity.java | 33 +- .../java/com/google/gcloud/datastore/Key.java | 177 ++++------ .../google/gcloud/datastore/KeyBuilder.java | 52 +-- .../gcloud/datastore/KeyPathElement.java | 101 ++++++ .../com/google/gcloud/datastore/KeyValue.java | 13 +- .../google/gcloud/datastore/ListValue.java | 13 +- .../google/gcloud/datastore/LongValue.java | 13 +- .../google/gcloud/datastore/NullValue.java | 12 +- .../gcloud/datastore/PartialEntity.java | 51 +-- .../google/gcloud/datastore/PartialKey.java | 315 +++--------------- .../com/google/gcloud/datastore/RawValue.java | 27 +- .../google/gcloud/datastore/StringValue.java | 13 +- .../com/google/gcloud/datastore/Value.java | 4 +- .../google/gcloud/datastore/package-info.java | 9 +- .../datastore/DatastoreServiceTest.java | 39 +-- .../gcloud/datastore/SerializationTest.java | 46 ++- 25 files changed, 622 insertions(+), 583 deletions(-) rename src/main/java/com/google/gcloud/datastore/{PropertyContainer.java => BaseEntity.java} (90%) create mode 100644 src/main/java/com/google/gcloud/datastore/BaseKey.java create mode 100644 src/main/java/com/google/gcloud/datastore/KeyPathElement.java diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java index 908710ceb750..826f59e1d300 100644 --- a/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/src/main/java/com/google/gcloud/ServiceOptions.java @@ -70,7 +70,7 @@ protected abstract static class Builder> { private HttpTransport httpTransport; private AuthConfig authConfig; - public Builder() {} + protected Builder() {} protected Builder(ServiceOptions options) { host = options.host; diff --git a/src/main/java/com/google/gcloud/datastore/PropertyContainer.java b/src/main/java/com/google/gcloud/datastore/BaseEntity.java similarity index 90% rename from src/main/java/com/google/gcloud/datastore/PropertyContainer.java rename to src/main/java/com/google/gcloud/datastore/BaseEntity.java index c2393ba2a741..7aae77d360e2 100644 --- a/src/main/java/com/google/gcloud/datastore/PropertyContainer.java +++ b/src/main/java/com/google/gcloud/datastore/BaseEntity.java @@ -11,23 +11,23 @@ import java.util.Set; /** - * A container of properties (name and Value pairs). + * A base class for entities. */ -abstract class PropertyContainer extends Serializable { +abstract class BaseEntity extends Serializable { private static final long serialVersionUID = 8175618724683792766L; private final transient ImmutableSortedMap> properties; - protected abstract static class Builder> { + protected abstract static class Builder> { private final Map> properties; - public Builder() { + protected Builder() { properties = new HashMap<>(); } - public Builder(PropertyContainer entity) { + protected Builder(BaseEntity entity) { properties = new HashMap<>(entity.properties()); } @@ -91,7 +91,7 @@ public B setPartialEntityProperty(String name, PartialEntity value) { return self(); } - public B setListProperty(String name, List> values) { + public B setListProperty(String name, List> values) { properties.put(name, new ListValue(values)); return self(); } @@ -113,7 +113,7 @@ public E build() { protected abstract E build(ImmutableSortedMap> properties); } - protected PropertyContainer(ImmutableSortedMap> properties) { + protected BaseEntity(ImmutableSortedMap> properties) { this.properties = properties; } @@ -208,10 +208,10 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (!(obj instanceof PropertyContainer)) { + if (!(obj instanceof BaseEntity)) { return false; } - return properties.equals(((PropertyContainer) obj).properties); + return properties.equals(((BaseEntity) obj).properties); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/BaseKey.java b/src/main/java/com/google/gcloud/datastore/BaseKey.java new file mode 100644 index 000000000000..47859cd619d4 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/BaseKey.java @@ -0,0 +1,193 @@ +package com.google.gcloud.datastore; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.gcloud.datastore.DatastoreServiceOptions.validateDataset; +import static com.google.gcloud.datastore.DatastoreServiceOptions.validateNamespace; + +import com.google.api.services.datastore.DatastoreV1; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; + +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +/** + * Base class for keys. + */ +abstract class BaseKey extends Serializable { + + private static final long serialVersionUID = -4671243265877410635L; + + private final transient String dataset; + private final transient String namespace; + private final transient ImmutableList ancestors; + private final transient String kind; + + abstract static class Builder> { + + private String dataset; + private String namespace; + private String kind; + private final List ancestors; + + private static final int MAX_PATH = 100; + + public Builder(String dataset, String kind) { + this.dataset = validateDataset(dataset); + this.kind = validateKind(kind); + ancestors = new LinkedList<>(); + } + + public Builder(BaseKey copyFrom) { + dataset = copyFrom.dataset(); + namespace = copyFrom.namespace(); + kind = copyFrom.kind(); + ancestors = new LinkedList<>(copyFrom.ancestors()); + } + + @SuppressWarnings("unchecked") + protected B self() { + return (B) this; + } + + public B addAncestor(String kind, long id) { + checkArgument(id != 0, "id must not be equal to zero"); + return addAncestor(new KeyPathElement(kind, id)); + } + + public B addAncestor(String kind, String name) { + checkArgument(Strings.isNullOrEmpty(name) , "name must not be empty or null"); + checkArgument(name.length() <= 500, "name must not exceed 500 characters"); + return addAncestor(new KeyPathElement(kind, name)); + } + + public B addAncestor(KeyPathElement... ancestor) { + Preconditions.checkState(ancestors.size() + ancestor.length <= MAX_PATH, + "path can have at most 100 elements"); + for (KeyPathElement pathElement : ancestor) { + ancestors.add(pathElement); + } + return self(); + } + + public B addAncestors(Iterable ancestors) { + for (KeyPathElement pathElement : ancestors) { + addAncestor(pathElement); + } + return self(); + } + + public B kind(String kind) { + this.kind = validateKind(kind); + return self(); + } + + private String validateKind(String kind) { + checkArgument(!Strings.isNullOrEmpty(kind), "kind must not be empty or null"); + checkArgument(kind.length() <= 500, "kind must not contain more than 500 characters"); + return kind; + } + + public B clearPath() { + ancestors.clear(); + return self(); + } + + public B dataset(String dataset) { + this.dataset = validateDataset(dataset); + return self(); + } + + public B namespace(String namespace) { + this.namespace = validateNamespace(namespace); + return self(); + } + + public K build() { + return build(dataset, namespace, ImmutableList.copyOf(ancestors), kind); + } + + protected abstract K build( + String dataset, String namespace, ImmutableList ancestors, String kind); + } + + BaseKey(String dataset, String namespace, ImmutableList ancestors, String kind) { + this.dataset = dataset; + this.namespace = namespace; + this.ancestors = ancestors; + this.kind = kind; + } + + /** + * Returns the key's dataset. + */ + public String dataset() { + return dataset; + } + + /** + * Returns the key's namespace or {@code null} if not provided. + */ + public String namespace() { + return namespace; + } + + /** + * Returns an immutable list with the key's ancestors. + */ + public List ancestors() { + return ancestors; + } + + /** + * Returns the key's kind. + */ + public String kind() { + return kind; + } + + @Override + public int hashCode() { + return Objects.hash(dataset, namespace, ancestors, kind); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BaseKey)) { + return false; + } + BaseKey other = (BaseKey) obj; + return Objects.equals(dataset, other.dataset) + && Objects.equals(namespace, other.namespace) + && Objects.equals(ancestors, other.ancestors) + && Objects.equals(kind, other.kind); + } + + @Override + protected DatastoreV1.Key toPb() { + DatastoreV1.Key.Builder keyPb = DatastoreV1.Key.newBuilder(); + DatastoreV1.PartitionId.Builder partitionIdPb = DatastoreV1.PartitionId.newBuilder(); + if (dataset != null) { + partitionIdPb.setDatasetId(dataset); + } + if (namespace != null) { + partitionIdPb.setNamespace(namespace); + } + if (partitionIdPb.hasDatasetId() || partitionIdPb.hasNamespace()) { + keyPb.setPartitionId(partitionIdPb.build()); + } + for (KeyPathElement pathEntry : ancestors) { + keyPb.addPathElement(pathEntry.toPb()); + } + addLeaf(keyPb); + return keyPb.build(); + } + + protected void addLeaf(DatastoreV1.Key.Builder keyPb) { + if (kind != null) { + keyPb.addPathElement(new KeyPathElement(kind).toPb()); + } + } +} diff --git a/src/main/java/com/google/gcloud/datastore/BlobValue.java b/src/main/java/com/google/gcloud/datastore/BlobValue.java index d44d384909a6..0b3a3fed9129 100644 --- a/src/main/java/com/google/gcloud/datastore/BlobValue.java +++ b/src/main/java/com/google/gcloud/datastore/BlobValue.java @@ -18,7 +18,7 @@ public int getProtoFieldId() { @Override public Builder newBuilder(Blob value) { - return new Builder(value); + return builder(value); } @Override @@ -34,9 +34,8 @@ protected void setValue(BlobValue from, DatastoreV1.Value.Builder to) { public static final class Builder extends Value.BaseBuilder { - public Builder(Blob blob) { + private Builder() { super(Type.BLOB); - set(blob); } @Override @@ -46,10 +45,14 @@ public BlobValue build() { } public BlobValue(Blob blob) { - this(new Builder(blob)); + this(builder(blob)); } - BlobValue(Builder builder) { + private BlobValue(Builder builder) { super(builder); } + + public static Builder builder(Blob blob) { + return new Builder().set(blob); + } } diff --git a/src/main/java/com/google/gcloud/datastore/BooleanValue.java b/src/main/java/com/google/gcloud/datastore/BooleanValue.java index 6eebdbf69b56..533ee14d5b0d 100644 --- a/src/main/java/com/google/gcloud/datastore/BooleanValue.java +++ b/src/main/java/com/google/gcloud/datastore/BooleanValue.java @@ -18,7 +18,7 @@ public int getProtoFieldId() { @Override public Builder newBuilder(Boolean value) { - return new Builder(value); + return builder(value); } @Override @@ -34,9 +34,8 @@ protected void setValue(BooleanValue from, DatastoreV1.Value.Builder to) { public static final class Builder extends Value.BaseBuilder { - public Builder(boolean value) { + private Builder() { super(Type.BOOLEAN); - set(value); } @Override @@ -46,10 +45,14 @@ public BooleanValue build() { } public BooleanValue(boolean value) { - this(new Builder(value)); + this(builder(value)); } - BooleanValue(Builder builder) { + private BooleanValue(Builder builder) { super(builder); } + + public static Builder builder(boolean value) { + return new Builder().set(value); + } } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index 69819b1c73f0..250fe0048360 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -87,13 +87,10 @@ protected Key computeNext() { } private PartialKey trimNameOrId(PartialKey key) { - if (key.getLeaf().nameOrId() == null) { - return key; + if (key instanceof Key) { + return PartialKey.builder(key).build(); } - return new PartialKey.Builder(key.dataset(), key.kind()) - .namespace(key.namespace()) - .addAncestors(key.ancestors()) - .build(); + return key; } @Override diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java index 8b2f906283d9..2f41df15a855 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java @@ -41,9 +41,10 @@ public static class Builder extends ServiceOptions.Builder { private String namespace; private boolean force = false; - public Builder() {} + private Builder() { + } - public Builder(DatastoreServiceOptions options) { + private Builder(DatastoreServiceOptions options) { super(options); dataset = options.dataset; force = options.force; @@ -125,4 +126,12 @@ DatastoreOptions toDatastoreOptions() { .initializer(httpRequestInitializer()) .build(); } + + public static Builder builder() { + return new Builder(); + } + + public static Builder builder(DatastoreServiceOptions options) { + return new Builder(options); + } } diff --git a/src/main/java/com/google/gcloud/datastore/DateTimeValue.java b/src/main/java/com/google/gcloud/datastore/DateTimeValue.java index fc2258c26c10..85ac6a853779 100644 --- a/src/main/java/com/google/gcloud/datastore/DateTimeValue.java +++ b/src/main/java/com/google/gcloud/datastore/DateTimeValue.java @@ -19,7 +19,7 @@ public int getProtoFieldId() { @Override public Builder newBuilder(DateTime value) { - return new Builder(value); + return builder(value); } @Override @@ -36,9 +36,8 @@ protected void setValue(DateTimeValue from, DatastoreV1.Value.Builder to) { public static final class Builder extends Value.BaseBuilder { - public Builder(DateTime dateTime) { + private Builder() { super(Type.DATE_TIME); - set(dateTime); } @Override @@ -48,10 +47,14 @@ public DateTimeValue build() { } public DateTimeValue(DateTime dateTime) { - this(new Builder(dateTime)); + this(builder(dateTime)); } - DateTimeValue(Builder builder) { + private DateTimeValue(Builder builder) { super(builder); } + + public static Builder builder(DateTime dateTime) { + return new Builder().set(dateTime); + } } diff --git a/src/main/java/com/google/gcloud/datastore/DoubleValue.java b/src/main/java/com/google/gcloud/datastore/DoubleValue.java index dca443a32851..b2f4e264c723 100644 --- a/src/main/java/com/google/gcloud/datastore/DoubleValue.java +++ b/src/main/java/com/google/gcloud/datastore/DoubleValue.java @@ -18,7 +18,7 @@ public int getProtoFieldId() { @Override public Builder newBuilder(Double value) { - return new Builder(value); + return builder(value); } @Override @@ -34,9 +34,8 @@ protected void setValue(DoubleValue from, DatastoreV1.Value.Builder to) { public static final class Builder extends Value.BaseBuilder { - public Builder(double value) { + public Builder() { super(Type.DOUBLE); - set(value); } @Override @@ -46,10 +45,14 @@ public DoubleValue build() { } public DoubleValue(double value) { - this(new Builder(value)); + this(builder(value)); } - DoubleValue(Builder builder) { + private DoubleValue(Builder builder) { super(builder); } + + public static Builder builder(double value) { + return new Builder().set(value); + } } diff --git a/src/main/java/com/google/gcloud/datastore/Entity.java b/src/main/java/com/google/gcloud/datastore/Entity.java index d6d671439d6f..f497cb6c7d67 100644 --- a/src/main/java/com/google/gcloud/datastore/Entity.java +++ b/src/main/java/com/google/gcloud/datastore/Entity.java @@ -20,27 +20,19 @@ public final class Entity extends PartialEntity { private static final long serialVersionUID = 432961565733066915L; - public static final class Builder extends PropertyContainer.Builder { + public static final class Builder extends BaseEntity.Builder { private Key key; - public Builder(Key key) { + private Builder(Key key) { this.key = checkNotNull(key); } - public Builder(Entity entity) { + private Builder(Entity entity) { super(entity); key = entity.key(); } - /** - * Create a Builder for the given key and with the properties from the given entity. - */ - public Builder(Key key, PartialEntity entity) { - super(entity); - this.key = key; - } - public Builder key(Key key) { this.key = checkNotNull(key); return this; @@ -52,7 +44,7 @@ protected Entity build(ImmutableSortedMap> properties) { } } - private Entity(Key key, ImmutableSortedMap> properties) { + Entity(Key key, ImmutableSortedMap> properties) { super(key, properties); } @@ -70,11 +62,16 @@ protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { } static Entity fromPb(DatastoreV1.Entity entityPb) { - Preconditions.checkArgument(entityPb.hasKey()); - Builder builder = new Builder(Key.fromPb(entityPb.getKey())); - for (DatastoreV1.Property property : entityPb.getPropertyList()) { - builder.setProperty(property.getName(), Value.fromPb(property.getValue())); - } - return builder.build(); + PartialEntity entity = PartialEntity.fromPb(entityPb); + Preconditions.checkState(entity instanceof Entity, "Entity is not complete"); + return (Entity) entity; + } + + public static Builder builder(Key key) { + return new Builder(key); + } + + public static Builder builder(Entity copyFrom) { + return new Builder(copyFrom); } } diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index 14c27114a589..c73f5d8819dc 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -3,6 +3,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.api.services.datastore.DatastoreV1; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; @@ -22,88 +23,33 @@ public final class Key extends PartialKey { private static final long serialVersionUID = 3160994559785491356L; - public static final class Builder extends PartialKey.Builder { + private final transient String name; + private final transient Long id; + + public static final class Builder extends BaseKey.Builder { private String name; private Long id; - public Builder(String dataset, String kind, String name) { + private Builder(String dataset, String kind, String name) { super(dataset, kind); this.name = name; } - public Builder(String dataset, String kind, long id) { + private Builder(String dataset, String kind, long id) { super(dataset, kind); this.id = id; } - public Builder(Key parent, String kind, String name) { - super(parent, kind); - this.name = name; - } - - public Builder(Key parent, String kind, long id) { - super(parent, kind); - this.id = id; - } - - public Builder(Key from) { - super(from); - if (from.hasId()) { - id = from.id(); + private Builder(Key copyFrom) { + super(copyFrom); + if (copyFrom.hasId()) { + id = copyFrom.id(); } else { - name = from.name(); + name = copyFrom.name(); } } - @Override - public Builder addAncestor(String kind, long id) { - super.addAncestor(kind, id); - return this; - } - - @Override - public Builder addAncestor(String kind, String name) { - super.addAncestor(kind, name); - return this; - } - - @Override - public Builder addAncestor(Ancestor... ancestor) { - super.addAncestor(ancestor); - return this; - } - - @Override - public Builder addAncestors(Iterable ancestors) { - super.addAncestors(ancestors); - return this; - } - - @Override - public Builder kind(String kind) { - super.kind(kind); - return this; - } - - @Override - public Builder clearPath() { - super.clearPath(); - return this; - } - - @Override - public Builder dataset(String dataset) { - super.dataset(dataset); - return this; - } - - @Override - public Builder namespace(String namespace) { - super.namespace(namespace); - return this; - } - public Builder name(String name) { this.name = name; id = null; @@ -117,48 +63,56 @@ public Builder id(long id) { } @Override - public Key build() { - PartialKey key = super.build(); - return id == null ? new Key(key, name) : new Key(key, id); + protected Key build(String dataset, String namespace, ImmutableList ancestors, + String kind) { + if (id == null) { + return new Key(dataset, namespace, ancestors, kind, name); + } + return new Key(dataset, namespace, ancestors, kind, id); } } - private Key(PartialKey key, String name) { - super(key.dataset(), key.namespace(), newPath(key, name)); + Key(String dataset, String namespace, ImmutableList ancestors, + String kind, String name) { + super(dataset, namespace, ancestors, kind); + this.name = name; + this.id = null; } - private Key(PartialKey key, long id) { - super(key.dataset(), key.namespace(), newPath(key, id)); + Key(String dataset, String namespace, ImmutableList ancestors, + String kind, long id) { + super(dataset, namespace, ancestors, kind); + this.id = id; + this.name = null; } public boolean hasId() { - return id() != null; + return id != null; } /** * Returns the key's id or {@code null} if it has a name instead. */ public Long id() { - return getLeaf().id(); + return id; } public boolean hasName() { - return name() != null; + return name != null; } /** * Returns the key's name or {@code null} if it has an id instead. */ public String name() { - return getLeaf().name(); + return name; } /** * Returns the key's id (as {@link #Long}) or name (as {@link String}). */ public Object nameOrId() { - Ancestor leaf = getLeaf(); - return leaf.hasId() ? leaf.id() : leaf.name(); + return hasId() ? id : name; } /** @@ -181,7 +135,7 @@ public static Key fromUrlSafe(String urlSafe) { try { String utf8Str = URLDecoder.decode(urlSafe, UTF_8.name()); DatastoreV1.Key keyPb = DatastoreV1.Key.parseFrom(ByteString.copyFromUtf8(utf8Str)); - return Key.fromPb(keyPb); + return fromPb(keyPb); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Unxpeced decoding exception", e); } catch (InvalidProtocolBufferException e) { @@ -189,45 +143,48 @@ public static Key fromUrlSafe(String urlSafe) { } } - /** - * Convert an {@code IncompleteKey} to a {@code Key} provided that the key has - * either name or id (complete). - - * @throws IllegalArgumentException if provided key is not complete. - */ - public static Key fromIncompleteKey(PartialKey key) { - if (key instanceof Key) { - return (Key) key; - } - Ancestor leaf = key.getLeaf(); - if (leaf.hasId()) { - return new Key(key, leaf.id()); - } else if (leaf.hasName()) { - return new Key(key, leaf.name()); - } - throw new IllegalArgumentException("Key is missing name or id"); - } - @Override protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { return fromPb(DatastoreV1.Key.parseFrom(bytesPb)); } static Key fromPb(DatastoreV1.Key keyPb) { - return fromIncompleteKey(PartialKey.fromPb(keyPb)); + PartialKey key = PartialKey.fromPb(keyPb); + Preconditions.checkState(key instanceof Key, "Key is not complete"); + return (Key) key; + } + + public static Builder builder(String dataset, String kind, String name) { + return new Builder(dataset, kind, name); } - private static ImmutableList newPath(PartialKey key, String name) { - ImmutableList.Builder path = ImmutableList.builder(); - path.addAll(key.ancestors()); - path.add(new Ancestor(key.kind(), name)); - return path.build(); + public static Builder builder(String dataset, String kind, long id) { + return new Builder(dataset, kind, id); } - private static ImmutableList newPath(PartialKey key, long id) { - ImmutableList.Builder path = ImmutableList.builder(); - path.addAll(key.ancestors()); - path.add(new Ancestor(key.kind(), id)); - return path.build(); + public static Builder builder(Key copyFrom) { + return new Builder(copyFrom); + } + + public static Builder builder(Key parent, String kind, String name) { + Builder builder = builder(parent.dataset(), kind, name); + addParentToBuilder(parent, builder); + return builder; + } + + public static Builder builder(Key parent, String kind, long id) { + Builder builder = builder(parent.dataset(), kind, id); + addParentToBuilder(parent, builder); + return builder; + } + + private static void addParentToBuilder(Key parent, Builder builder) { + builder.namespace(parent.namespace()); + builder.addAncestors(parent.ancestors()); + if (parent.hasId()) { + builder.addAncestor(new KeyPathElement(parent.kind(), parent.id())); + } else { + builder.addAncestor(new KeyPathElement(parent.kind(), parent.name())); + } } } diff --git a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java index 8b9954717858..855c09d3358f 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java +++ b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java @@ -2,13 +2,13 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.gcloud.datastore.PartialKey.Ancestor; +import com.google.common.collect.ImmutableList; /** * An helper for creating keys for a specific {@link DatastoreService}, * using its associated dataset and namespace. */ -public final class KeyBuilder extends PartialKey.Builder { +public final class KeyBuilder extends BaseKey.Builder { private final DatastoreService service; @@ -22,55 +22,17 @@ public KeyBuilder(DatastoreService service, String kind) { } @Override - public KeyBuilder kind(String kind) { - super.kind(kind); - return this; - } - - @Override - public KeyBuilder addAncestor(String kind, String name) { - super.addAncestor(kind, name); - return this; - } - - @Override - public KeyBuilder addAncestor(String kind, long id) { - super.addAncestor(kind, id); - return this; - } - - @Override - public KeyBuilder namespace(String namespace) { - super.namespace(namespace); - return this; - } - - @Override - public KeyBuilder addAncestor(Ancestor... ancestor) { - super.addAncestor(ancestor); - return this; - } - - @Override - public KeyBuilder addAncestors(Iterable ancestors) { - super.addAncestors(ancestors); - return this; + protected PartialKey build(String dataset, String namespace, + ImmutableList ancestors, String kind) { + return new PartialKey(dataset, namespace, ancestors, kind); } public Key build(String name) { - PartialKey key = build(); - return new Key.Builder(key.dataset(), key.kind(), name) - .namespace(key.namespace()) - .addAncestors(key.ancestors()) - .build(); + return build().newKey(name); } public Key build(long id) { - PartialKey key = build(); - return new Key.Builder(key.dataset(), key.kind(), id) - .namespace(key.namespace()) - .addAncestors(key.ancestors()) - .build(); + return build().newKey(id); } /** diff --git a/src/main/java/com/google/gcloud/datastore/KeyPathElement.java b/src/main/java/com/google/gcloud/datastore/KeyPathElement.java new file mode 100644 index 000000000000..78b529ed6e7b --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/KeyPathElement.java @@ -0,0 +1,101 @@ +package com.google.gcloud.datastore; + +import com.google.api.services.datastore.DatastoreV1; +import com.google.protobuf.InvalidProtocolBufferException; + +import java.util.Objects; + +/** + * Represents a single element in a key's path. + */ +public final class KeyPathElement extends Serializable { + + private static final long serialVersionUID = -7968078857690784595L; + + private final transient String kind; + private final transient Long id; + private final transient String name; + + KeyPathElement(String kind) { + this(kind, null); + } + + public KeyPathElement(String kind, long id) { + this.kind = kind; + this.id = id; + name = null; + } + + public KeyPathElement(String kind, String name) { + this.kind = kind; + this.name = name; + id = null; + } + + public String kind() { + return kind; + } + + public boolean hasId() { + return id != null; + } + + public Long id() { + return id; + } + + public boolean hasName() { + return name != null; + } + + public String name() { + return name; + } + + public Object nameOrId() { + return id == null ? name : id; + } + + @Override + public int hashCode() { + return Objects.hash(kind, id, name); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof KeyPathElement)) { + return false; + } + KeyPathElement other = (KeyPathElement) obj; + return Objects.equals(kind, other.kind) + && Objects.equals(id, other.id) + && Objects.equals(name, other.name); + } + + @Override + protected DatastoreV1.Key.PathElement toPb() { + DatastoreV1.Key.PathElement.Builder pathElementPb = DatastoreV1.Key.PathElement.newBuilder(); + pathElementPb.setKind(kind); + if (id != null) { + pathElementPb.setId(id); + } else if (name != null) { + pathElementPb.setName(name); + } + return pathElementPb.build(); + } + + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + return fromPb(DatastoreV1.Key.PathElement.parseFrom(bytesPb)); + } + + static KeyPathElement fromPb(DatastoreV1.Key.PathElement pathElementPb) { + String kind = pathElementPb.getKind(); + if (pathElementPb.hasId()) { + return new KeyPathElement(kind, pathElementPb.getId()); + } else if (pathElementPb.hasName()) { + return new KeyPathElement(kind, pathElementPb.getName()); + } + return new KeyPathElement(kind); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/KeyValue.java b/src/main/java/com/google/gcloud/datastore/KeyValue.java index 671e9c355b14..8a6db4ff74a4 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyValue.java +++ b/src/main/java/com/google/gcloud/datastore/KeyValue.java @@ -18,7 +18,7 @@ public int getProtoFieldId() { @Override public Builder newBuilder(Key key) { - return new Builder(key); + return builder(key); } @Override @@ -34,9 +34,8 @@ protected void setValue(KeyValue from, DatastoreV1.Value.Builder to) { public static final class Builder extends Value.BaseBuilder { - public Builder(Key key) { + public Builder() { super(Type.KEY); - set(key); } @Override @@ -46,10 +45,14 @@ public KeyValue build() { } public KeyValue(Key key) { - this(new Builder(key)); + this(builder(key)); } - KeyValue(Builder builder) { + private KeyValue(Builder builder) { super(builder); } + + public static Builder builder(Key key) { + return new Builder().set(key); + } } diff --git a/src/main/java/com/google/gcloud/datastore/ListValue.java b/src/main/java/com/google/gcloud/datastore/ListValue.java index ef5c64a02e4f..ee829a71dcc0 100644 --- a/src/main/java/com/google/gcloud/datastore/ListValue.java +++ b/src/main/java/com/google/gcloud/datastore/ListValue.java @@ -24,7 +24,7 @@ public int getProtoFieldId() { @Override public Builder newBuilder(List> values) { - return new Builder().set(values); + return builder().set(values); } @Override @@ -49,9 +49,8 @@ public static final class Builder extends private ImmutableList.Builder> listBuilder = ImmutableList.builder(); - public Builder() { + private Builder() { super(Type.LIST); - indexed(false); } public Builder addValue(Value value) { @@ -95,14 +94,18 @@ public ListValue build() { } public ListValue(List> properties) { - this(new Builder().set(properties)); + this(builder().set(properties)); } public ListValue(Value first, Value... other) { this(new Builder().addValue(first, other)); } - ListValue(Builder builder) { + private ListValue(Builder builder) { super(builder); } + + public static Builder builder() { + return new Builder().indexed(false); + } } diff --git a/src/main/java/com/google/gcloud/datastore/LongValue.java b/src/main/java/com/google/gcloud/datastore/LongValue.java index 9a4690a24133..97b789e344bd 100644 --- a/src/main/java/com/google/gcloud/datastore/LongValue.java +++ b/src/main/java/com/google/gcloud/datastore/LongValue.java @@ -18,7 +18,7 @@ public int getProtoFieldId() { @Override public Builder newBuilder(Long value) { - return new Builder(value); + return builder(value); } @Override @@ -34,9 +34,8 @@ protected void setValue(LongValue from, DatastoreV1.Value.Builder to) { public static final class Builder extends Value.BaseBuilder { - public Builder(long value) { + private Builder() { super(Type.LONG); - set(value); } @Override @@ -46,10 +45,14 @@ public LongValue build() { } public LongValue(long value) { - this(new Builder(value)); + this(builder(value)); } - LongValue(Builder builder) { + private LongValue(Builder builder) { super(builder); } + + public static Builder builder(long value) { + return new Builder().set(value); + } } diff --git a/src/main/java/com/google/gcloud/datastore/NullValue.java b/src/main/java/com/google/gcloud/datastore/NullValue.java index fb60ab20042f..bb2c084522ae 100644 --- a/src/main/java/com/google/gcloud/datastore/NullValue.java +++ b/src/main/java/com/google/gcloud/datastore/NullValue.java @@ -13,7 +13,7 @@ public final class NullValue extends Value { @Override public Builder newBuilder(Void value) { - return new Builder(); + return builder(); } @Override @@ -34,7 +34,7 @@ protected void setValue(NullValue from, DatastoreV1.Value.Builder to) { public static final class Builder extends Value.BaseBuilder { - public Builder() { + private Builder() { super(Type.NULL); } @@ -51,10 +51,14 @@ public Builder set(Void value) { } public NullValue() { - this(new Builder()); + this(builder()); } - NullValue(Builder builder) { + private NullValue(Builder builder) { super(builder); } + + public static Builder builder() { + return new Builder(); + } } diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java index 8e1e8578e291..0796b2c9f5a1 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java @@ -13,36 +13,24 @@ * A partial entity also can be associated with a key (partial or full). * This class is immutable. */ -public class PartialEntity extends PropertyContainer { +public class PartialEntity extends BaseEntity { private static final long serialVersionUID = 6492561268709192891L; private final transient PartialKey key; - public static class Builder extends PropertyContainer.Builder { + public static class Builder extends BaseEntity.Builder { private PartialKey key; - public Builder() { + private Builder() { } - /** - * Construct a builder with a partial key (could be null). - */ - public Builder(PartialKey key) { - this.key = key; - } - - public Builder(PartialEntity entity) { + private Builder(PartialEntity entity) { super(entity); key = entity.key(); } - public Builder(PartialKey key, PartialEntity entity) { - super(entity); - this.key = key; - } - public Builder key(PartialKey key) { this.key = key; return this; @@ -59,6 +47,10 @@ protected PartialEntity(PartialKey key, ImmutableSortedMap>copyOf(properties())); + } + /** * Returns the key for this entity or {@code null} if it does not have one. */ @@ -94,11 +86,30 @@ protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { } static PartialEntity fromPb(DatastoreV1.Entity entityPb) { - PartialKey key = entityPb.hasKey() ? PartialKey.fromPb(entityPb.getKey()) : null; - Builder builder = new Builder(key); + ImmutableSortedMap.Builder> properties = + ImmutableSortedMap.naturalOrder(); for (DatastoreV1.Property property : entityPb.getPropertyList()) { - builder.setProperty(property.getName(), Value.fromPb(property.getValue())); + properties.put(property.getName(), Value.fromPb(property.getValue())); + } + PartialKey partialKey = null; + if (entityPb.hasKey()) { + partialKey = PartialKey.fromPb(entityPb.getKey()); + if (partialKey instanceof Key) { + return new Entity((Key) partialKey, properties.build()); + } } - return builder.build(); + return new PartialEntity(partialKey, properties.build()); + } + + public static Builder builder() { + return new Builder(); + } + + public static Builder builder(PartialKey key) { + return new Builder().key(key); + } + + public static Builder builder(PartialEntity copyFrom) { + return new Builder(copyFrom); } } diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index 51ece279e54c..129dbdf6e87d 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -1,267 +1,49 @@ package com.google.gcloud.datastore; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.gcloud.datastore.DatastoreServiceOptions.validateDataset; -import static com.google.gcloud.datastore.DatastoreServiceOptions.validateNamespace; - import com.google.api.services.datastore.DatastoreV1; -import com.google.common.base.Preconditions; -import com.google.common.base.Strings; +import com.google.api.services.datastore.DatastoreV1.Key.PathElement; import com.google.common.collect.ImmutableList; import com.google.protobuf.InvalidProtocolBufferException; -import java.util.LinkedList; import java.util.List; -import java.util.Objects; /** * A partial key (without a name or id). * Could be used as metadata for {@link PartialEntity}. * This class is immutable. */ -public class PartialKey extends Serializable { +public class PartialKey extends BaseKey { private static final long serialVersionUID = -75301206578793347L; - private final transient String dataset; - private final transient String namespace; - private final transient ImmutableList ancestors; - - public static final class Ancestor extends Serializable { - - private static final long serialVersionUID = -7968078857690784595L; - - private final transient String kind; - private final transient Long id; - private final transient String name; - - private Ancestor(String kind) { - this(kind, null); - } - - public Ancestor(String kind, long id) { - this.kind = kind; - this.id = id; - name = null; - } - - public Ancestor(String kind, String name) { - this.kind = kind; - this.name = name; - id = null; - } - - public String kind() { - return kind; - } - - public boolean hasId() { - return id != null; - } - - public Long id() { - return id; - } - - public boolean hasName() { - return name != null; - } - - public String name() { - return name; - } - - public Object nameOrId() { - return id == null ? name : id; - } - - @Override - public int hashCode() { - return Objects.hash(kind, id, name); - } + public static class Builder extends BaseKey.Builder { - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Ancestor)) { - return false; - } - Ancestor other = (Ancestor) obj; - return Objects.equals(kind, other.kind) - && Objects.equals(id, other.id) - && Objects.equals(name, other.name); + private Builder(String dataset, String kind) { + super(dataset, kind); } - @Override - protected DatastoreV1.Key.PathElement toPb() { - DatastoreV1.Key.PathElement.Builder pathElementPb = DatastoreV1.Key.PathElement.newBuilder(); - pathElementPb.setKind(kind); - if (id != null) { - pathElementPb.setId(id); - } else if (name != null) { - pathElementPb.setName(name); - } - return pathElementPb.build(); + private Builder(PartialKey copyFrom) { + super(copyFrom); } @Override - protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { - return fromPb(DatastoreV1.Key.PathElement.parseFrom(bytesPb)); - } - - static Ancestor fromPb(DatastoreV1.Key.PathElement pathElementPb) { - String kind = pathElementPb.getKind(); - if (pathElementPb.hasId()) { - return new Ancestor(kind, pathElementPb.getId()); - } else if (pathElementPb.hasName()) { - return new Ancestor(kind, pathElementPb.getName()); - } - return new Ancestor(kind); + protected PartialKey build(String dataset, String namespace, + ImmutableList ancestors, String kind) { + return new PartialKey(dataset, namespace, ancestors, kind); } } - public static class Builder { - - private String dataset; - private String namespace; - private String kind; - private final List ancestors = new LinkedList<>(); - - private static final int MAX_PATH = 100; - - public Builder(String dataset, String kind) { - this.dataset = validateDataset(dataset); - this.kind = validateKind(kind); - } - - public Builder(Key parent, String kind) { - dataset = parent.dataset(); - namespace = parent.namespace(); - ancestors.addAll(parent.ancestors()); - ancestors.add(parent.getLeaf()); - this.kind = kind; - } - - public Builder(PartialKey from) { - dataset = from.dataset(); - namespace = from.namespace(); - kind = from.kind(); - ancestors.addAll(from.ancestors()); - } - - public Builder addAncestor(String kind, long id) { - checkArgument(id != 0, "id must not be equal to zero"); - return addAncestor(new Ancestor(kind, id)); - } - - public Builder addAncestor(String kind, String name) { - checkArgument(Strings.isNullOrEmpty(name) , "name must not be empty or null"); - checkArgument(name.length() <= 500, "name must not exceed 500 characters"); - return addAncestor(new Ancestor(kind, name)); - } - - public Builder addAncestor(Ancestor... ancestor) { - Preconditions.checkState(ancestors.size() + ancestor.length <= MAX_PATH, - "path can have at most 100 elements"); - for (Ancestor pathElement : ancestor) { - ancestors.add(pathElement); - } - return this; - } - - public Builder addAncestors(Iterable ancestors) { - for (Ancestor pathElement : ancestors) { - addAncestor(pathElement); - } - return this; - } - - public Builder kind(String kind) { - this.kind = validateKind(kind); - return this; - } - - private String validateKind(String kind) { - checkArgument(!Strings.isNullOrEmpty(kind), "kind must not be empty or null"); - checkArgument(kind.length() <= 500, "kind must not contain more than 500 characters"); - return kind; - } - - public Builder clearPath() { - ancestors.clear(); - return this; - } - - public Builder dataset(String dataset) { - this.dataset = validateDataset(dataset); - return this; - } - - public Builder namespace(String namespace) { - this.namespace = validateNamespace(namespace); - return this; - } - - public PartialKey build() { - Ancestor leaf = new Ancestor(kind); - ImmutableList pathList = - ImmutableList.builder().addAll(ancestors).add(leaf).build(); - return new PartialKey(dataset, namespace, pathList); - } - } - - PartialKey(String dataset, String namespace, ImmutableList path) { - checkState(!path.isEmpty(), "path must not be empty"); - this.dataset = dataset; - this.namespace = namespace; - this.ancestors = path; - } - - /** - * Returns the key's dataset. - */ - public String dataset() { - return dataset; - } - - /** - * Returns the key's namespace or {@code null} if not provided. - */ - public String namespace() { - return namespace; - } - - /** - * Returns an immutable list with the key's ancestors. - */ - public List ancestors() { - return ancestors.subList(0, ancestors.size() - 1); - } - - /** - * Returns the key's kind. - */ - public String kind() { - return getLeaf().kind(); + PartialKey(String dataset, String namespace, ImmutableList ancestors, + String kind) { + super(dataset, namespace, ancestors, kind); } public Key newKey(String name) { - return new Key.Builder(dataset(), kind(), name) - .namespace(namespace()) - .addAncestors(ancestors()) - .build(); + return new Key(dataset(), namespace(), ImmutableList.copyOf(ancestors()), kind(), name); } public Key newKey(long id) { - return new Key.Builder(dataset(), kind(), id) - .namespace(namespace()) - .addAncestors(ancestors()) - .build(); - } - - @Override - public int hashCode() { - return Objects.hash(dataset, namespace, ancestors); + return new Key(dataset(), namespace(), ImmutableList.copyOf(ancestors()), kind(), id); } @Override @@ -269,33 +51,7 @@ public boolean equals(Object obj) { if (!(obj instanceof PartialKey)) { return false; } - PartialKey other = (PartialKey) obj; - return Objects.equals(dataset, other.dataset) - && Objects.equals(namespace, other.namespace) - && Objects.equals(ancestors, other.ancestors); - } - - @Override - protected DatastoreV1.Key toPb() { - return toPb(this).build(); - } - - static DatastoreV1.Key.Builder toPb(PartialKey key) { - DatastoreV1.Key.Builder keyPb = DatastoreV1.Key.newBuilder(); - DatastoreV1.PartitionId.Builder partitionIdPb = DatastoreV1.PartitionId.newBuilder(); - if (key.dataset != null) { - partitionIdPb.setDatasetId(key.dataset); - } - if (key.namespace != null) { - partitionIdPb.setNamespace(key.namespace); - } - if (partitionIdPb.hasDatasetId() || partitionIdPb.hasNamespace()) { - keyPb.setPartitionId(partitionIdPb.build()); - } - for (Ancestor pathEntry : key.ancestors) { - keyPb.addPathElement(pathEntry.toPb()); - } - return keyPb; + return super.equals(obj); } @Override @@ -315,14 +71,41 @@ static PartialKey fromPb(DatastoreV1.Key keyPb) { namespace = partitionIdPb.getNamespace(); } } - ImmutableList.Builder pathBuilder = ImmutableList.builder(); - for (DatastoreV1.Key.PathElement pathElementPb : keyPb.getPathElementList()) { - pathBuilder.add(Ancestor.fromPb(pathElementPb)); + List pathElementsPb = keyPb.getPathElementList(); + if (pathElementsPb.isEmpty()) { + return new PartialKey(dataset, namespace, ImmutableList.of(), null); + } + ImmutableList.Builder pathBuilder = ImmutableList.builder(); + for (int i = 0; i < pathElementsPb.size() - 1; i++) { + pathBuilder.add(KeyPathElement.fromPb(pathElementsPb.get(i))); + } + PathElement leaf = pathElementsPb.get(pathElementsPb.size() - 1); + String kind = leaf.getKind(); + if (leaf.hasId()) { + return new Key(dataset, namespace, pathBuilder.build(), kind, leaf.getId()); + } else if (leaf.hasName()) { + return new Key(dataset, namespace, pathBuilder.build(), kind, leaf.getName()); } - return new PartialKey(dataset, namespace, pathBuilder.build()); + return new PartialKey(dataset, namespace, pathBuilder.build(), kind); } - Ancestor getLeaf() { - return ancestors.get(ancestors.size() - 1); + public static Builder builder(String dataset, String kind) { + return new Builder(dataset, kind); + } + + public static Builder builder(PartialKey copyFrom) { + return new Builder(copyFrom); + } + + public static Builder builder(Key parent, String kind) { + Builder builder = new Builder(parent.dataset(), kind) + .namespace(parent.namespace()) + .addAncestors(parent.ancestors()); + if (parent.hasId()) { + builder.addAncestor(new KeyPathElement(parent.kind(), parent.id())); + } else { + builder.addAncestor(new KeyPathElement(parent.kind(), parent.name())); + } + return builder; } } diff --git a/src/main/java/com/google/gcloud/datastore/RawValue.java b/src/main/java/com/google/gcloud/datastore/RawValue.java index 120ad7085e4e..aa4c2229e96c 100644 --- a/src/main/java/com/google/gcloud/datastore/RawValue.java +++ b/src/main/java/com/google/gcloud/datastore/RawValue.java @@ -11,7 +11,7 @@ public final class RawValue extends Value { - Builder(DatastoreV1.Value valuePb) { + private Builder() { super(Type.RAW_VALUE); - if (valuePb.hasIndexed()) { - indexed(valuePb.getIndexed()); - } - if (valuePb.hasMeaning()) { - meaning(valuePb.getMeaning()); - } - set(valuePb); } @Override @@ -49,11 +42,23 @@ public RawValue build() { } } - RawValue(Builder builder) { + private RawValue(Builder builder) { super(builder); } RawValue(DatastoreV1.Value valuePb) { - this(new Builder(valuePb)); + this(builder(valuePb)); + } + + static Builder builder(DatastoreV1.Value valuePb) { + Builder builder = new Builder(); + if (valuePb.hasIndexed()) { + builder.indexed(valuePb.getIndexed()); + } + if (valuePb.hasMeaning()) { + builder.meaning(valuePb.getMeaning()); + } + builder.set(valuePb); + return builder; } } diff --git a/src/main/java/com/google/gcloud/datastore/StringValue.java b/src/main/java/com/google/gcloud/datastore/StringValue.java index 7f8ee6e979d8..4d35aa3e3e01 100644 --- a/src/main/java/com/google/gcloud/datastore/StringValue.java +++ b/src/main/java/com/google/gcloud/datastore/StringValue.java @@ -19,7 +19,7 @@ public int getProtoFieldId() { @Override public Builder newBuilder(String value) { - return new Builder(value); + return builder(value); } @Override @@ -35,9 +35,8 @@ protected void setValue(StringValue from, DatastoreV1.Value.Builder to) { public static final class Builder extends Value.BaseBuilder { - public Builder(String value) { + private Builder() { super(Type.STRING); - set(value); } @Override @@ -50,10 +49,14 @@ public StringValue build() { } public StringValue(String value) { - this(new Builder(value)); + this(builder(value)); } - StringValue(Builder builder) { + private StringValue(Builder builder) { super(builder); } + + public static Builder builder(String value) { + return new Builder().set(value); + } } diff --git a/src/main/java/com/google/gcloud/datastore/Value.java b/src/main/java/com/google/gcloud/datastore/Value.java index 03b40e61d98e..d35820e38db7 100644 --- a/src/main/java/com/google/gcloud/datastore/Value.java +++ b/src/main/java/com/google/gcloud/datastore/Value.java @@ -16,7 +16,7 @@ /** * Base class for all Google Cloud Datastore value types. * All values must be associated with a non-null content (except {@link NullValue}). - * All values are immutable (including their content). To edit (a copy) use {@link #builder()}. + * All values are immutable (including their content). To edit (a copy) use {@link #toBuilder()}. * Unsupported value (deprecated or unrecognized) would be represented by {@link RawValue}. * * @param the type of the content for this value @@ -298,7 +298,7 @@ public final V get() { } @SuppressWarnings("unchecked") - public final B builder() { + public final B toBuilder() { BuilderFactory builderFactory = type().getBuilderFactory(); B builder = builderFactory.newBuilder(get()); return builder.mergeFrom((P) this); diff --git a/src/main/java/com/google/gcloud/datastore/package-info.java b/src/main/java/com/google/gcloud/datastore/package-info.java index 23b61cade70c..60aaddb13a74 100644 --- a/src/main/java/com/google/gcloud/datastore/package-info.java +++ b/src/main/java/com/google/gcloud/datastore/package-info.java @@ -2,15 +2,15 @@ * A client to the Google Cloud Datastore. * Typical usage would be: *
 {@code
- * DatastoreServiceOptions options = new DatastoreServiceOptions.Builder().dataset(DATASET).build();
+ * DatastoreServiceOptions options = DatastoreServiceOptions.builder().dataset(DATASET).build();
  * DatastoreService datastore = DatastoreServiceFactory.getDefault(options);
  * KeyBuilder keyBuilder = datastore.newKeyBuilder(kind);
  * Key key = keyBuilder.build(keyName);
  * Entity entity = datastore.get(key);
  * if (entity == null) {
- *   entity = new Entity.Builder(key)
+ *   entity = Entity.builder(key)
  *       .setStringProperty("name", "John Do")
- *       .setProperty("age", new LongValue.Builder(100).indexed(false).build())
+ *       .setProperty("age", LongValue.builder(100).indexed(false).build())
  *       .setBooleanProperty("updated", false)
  *       .build();
  *   datastore.put(entity);
@@ -20,7 +20,7 @@
  *     String[] name = entity.stringProperty("name").split(" ");
  *     entity = entity.builder()
  *         .setStringProperty("name", name[0])
- *         .setProperty("last_name", new StringProperty.Builder(name[1]).indexed(false).build())
+ *         .setProperty("last_name", StringProperty.builder(name[1]).indexed(false).build())
  *         .setBooleanProperty("updated", true)
  *         .removeProperty("old_property")
  *         .setDoubleProperty("new_property", 1.1)
@@ -29,6 +29,5 @@
  *   }
  * }
  * } 
- * */ package com.google.gcloud.datastore; diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index 3cc103450e0c..970da50d56d5 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -23,35 +23,36 @@ public class DatastoreServiceTest { private static final String KIND2 = "kind2"; private static final NullValue NULL_VALUE = new NullValue(); private static final StringValue STR_VALUE = new StringValue("str"); - private static final BooleanValue BOOL_VALUE = - new BooleanValue.Builder(false).indexed(false).build(); - private static final PartialKey PARTIAL_KEY1 = new PartialKey.Builder(DATASET, KIND1).build(); - private static final PartialKey PARTIAL_KEY2 = new PartialKey.Builder(DATASET, KIND2).build(); + private static final BooleanValue BOOL_VALUE = BooleanValue.builder(false).indexed(false).build(); + private static final PartialKey PARTIAL_KEY1 = PartialKey.builder(DATASET, KIND1).build(); + private static final PartialKey PARTIAL_KEY2 = PartialKey.builder(DATASET, KIND2).build(); private static final Key KEY1 = PARTIAL_KEY1.newKey("name"); - private static final Key KEY2 = new Key.Builder(KEY1, KIND2, 1).build(); - private static final Key KEY3 = KEY2.builder().name("bla").build(); + private static final Key KEY2 = Key.builder(KEY1, KIND2, 1).build(); + private static final Key KEY3 = Key.builder(KEY2).name("bla").build(); private static final KeyValue KEY_VALUE = new KeyValue(KEY1); - private static final ListValue LIST_VALUE1 = new ListValue.Builder() + private static final ListValue LIST_VALUE1 = ListValue.builder() .addValue(NULL_VALUE) .addValue(STR_VALUE, BOOL_VALUE) .build(); private static final ListValue LIST_VALUE2 = new ListValue(Collections.singletonList(KEY_VALUE)); - private static final PartialEntity PARTIAL_ENTITY1 = new PartialEntity.Builder(PARTIAL_KEY2) + private static final PartialEntity PARTIAL_ENTITY1 = PartialEntity.builder(PARTIAL_KEY2) .setProperty("str", STR_VALUE) .setProperty("bool", BOOL_VALUE) .setProperty("list", LIST_VALUE1) .build(); - private static final Entity ENTITY1 = new Entity.Builder(KEY1) + private static final Entity ENTITY1 = Entity.builder(KEY1) .setProperty("str", STR_VALUE) .setProperty("bool", BOOL_VALUE) .setProperty("partial1", new PartialEntityValue(PARTIAL_ENTITY1)) .setProperty("list", LIST_VALUE2) .build(); - private static final Entity ENTITY2 = new Entity.Builder(KEY2, ENTITY1) + private static final Entity ENTITY2 = Entity.builder(ENTITY1) + .key(KEY2) .removeProperty("str") .setProperty("null", NULL_VALUE) .build(); - private static final Entity ENTITY3 = new Entity.Builder(KEY3, ENTITY1) + private static final Entity ENTITY3 = Entity.builder(ENTITY1) + .key(KEY3) .removeProperty("str") .setProperty("null", NULL_VALUE) .setProperty("partial2", new PartialEntityValue(ENTITY1)) @@ -67,7 +68,7 @@ public void setUp() { // reference: https://cloud.google.com/datastore/docs/tools/devserver // Or even better, using a "GCE_HOME" param/env initiate and destroy the server // before and after tests via ant or maven - options = new DatastoreServiceOptions.Builder() + options = DatastoreServiceOptions.builder() .dataset(DATASET) .host("http://localhost:8080") .build(); @@ -96,15 +97,15 @@ public void testNewTransactionRollback() { @Test public void testNewBatchWriter() { BatchWriter batchWriter = datastore.newBatchWriter(); - Entity entity1 = new Entity.Builder(ENTITY1).clearProperties().build(); - Entity entity2 = new Entity.Builder(ENTITY2) + Entity entity1 = Entity.builder(ENTITY1).clearProperties().build(); + Entity entity2 = Entity.builder(ENTITY2) .clearProperties() .setNullProperty("bla") .build(); - Entity entity4 = new Entity.Builder(KEY2.newKey("newName1")) + Entity entity4 = Entity.builder(KEY2.newKey("newName1")) .setProperty("value", new StringValue("value")) .build(); - Entity entity5 = new Entity.Builder(KEY2.newKey("newName2")) + Entity entity5 = Entity.builder(KEY2.newKey("newName2")) .setStringProperty("value", "value") .build(); batchWriter.add(entity4, entity5); @@ -244,7 +245,7 @@ public void testUpdate() { } datastore.add(ENTITY3); assertEquals(ENTITY3, datastore.get(ENTITY3.key())); - Entity entity3 = new Entity.Builder(ENTITY3) + Entity entity3 = Entity.builder(ENTITY3) .clearProperties() .setProperty("bla", new NullValue()) .build(); @@ -261,7 +262,7 @@ public void testPut() { assertNull(keys.next()); assertFalse(keys.hasNext()); - Entity entity2 = new Entity.Builder(ENTITY2) + Entity entity2 = Entity.builder(ENTITY2) .clearProperties() .setProperty("bla", new NullValue()) .build(); @@ -293,7 +294,7 @@ public void testDelete() { public void testNewKeyBuilder() { KeyBuilder keyBuilder = datastore.newKeyBuilder(KIND1); assertEquals(PARTIAL_KEY1, keyBuilder.build()); - assertEquals(PARTIAL_KEY1.builder().kind(KIND2).build(), + assertEquals(PartialKey.builder(PARTIAL_KEY1).kind(KIND2).build(), datastore.newKeyBuilder(KIND2).build()); assertEquals(KEY1, keyBuilder.build("name")); assertEquals(KEY1.builder().id(2).build(), keyBuilder.build(2)); diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 58a2b14d4d1c..a7b7c87baf7f 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -18,14 +18,13 @@ public class SerializationTest { private static final PartialKey INCOMPLETE_KEY1 = - new PartialKey.Builder("ds", "k").addAncestor("p", 1).build(); - private static final Key KEY1 = new Key.Builder("ds", "k", "n").build(); + PartialKey.builder("ds", "k").addAncestor("p", 1).build(); + private static final Key KEY1 = Key.builder("ds", "k", "n").build(); private static final PartialKey INCOMPLETE_KEY2 = - new PartialKey.Builder(KEY1, "v").addAncestor("p", 1).build(); - private static final Key KEY2 = new Key.Builder(KEY1, "v", 2).build(); + PartialKey.builder(KEY1, "v").addAncestor("p", 1).build(); + private static final Key KEY2 = Key.builder(KEY1, "v", 2).build(); private static final KeyValue KEY_VALUE = new KeyValue(KEY1); - private static final NullValue NULL_VALUE = - new NullValue.Builder().indexed(true).build(); + private static final NullValue NULL_VALUE = NullValue.builder().indexed(true).build(); private static final StringValue STRING_VALUE = new StringValue("hello"); private static final LongValue LONG_VALUE = new LongValue(123); private static final DoubleValue DOUBLE_VALUE = new DoubleValue(12.34); @@ -36,34 +35,31 @@ public class SerializationTest { new BlobValue(Blob.copyFrom(new byte[] {10, 0, -2})); private static final RawValue RAW_VALUE = new RawValue( DatastoreV1.Value.newBuilder().setBlobKeyValue("blob-key").setMeaning(18).build()); - private static final Entity ENTITY1 = new Entity.Builder(KEY1).build(); + private static final Entity ENTITY1 = Entity.builder(KEY1).build(); private static final Entity ENTITY2 = - new Entity.Builder(KEY2).setProperty("null", new NullValue()).build(); - private static final Entity ENTITY3 = - new Entity.Builder(KEY2) - .setProperty("p1", new StringValue.Builder("hi1").meaning(10).build()) - .setProperty("p2", new StringValue.Builder("hi2").meaning(11).indexed(false).build()) - .setProperty("p3", new LongValue.Builder(100).indexed(false).meaning(100).build()) - .build(); + Entity.builder(KEY2).setProperty("null", new NullValue()).build(); + private static final Entity ENTITY3 = Entity.builder(KEY2) + .setProperty("p1", StringValue.builder("hi1").meaning(10).build()) + .setProperty("p2", StringValue.builder("hi2").meaning(11).indexed(false).build()) + .setProperty("p3", LongValue.builder(100).indexed(false).meaning(100).build()) + .build(); private static final PartialEntity EMBEDDED_ENTITY1 = ENTITY1; private static final PartialEntity EMBEDDED_ENTITY2 = ENTITY2; - private static final PartialEntity EMBEDDED_ENTITY3 = - new PartialEntity.Builder(INCOMPLETE_KEY1) - .setProperty("p1", STRING_VALUE) - .setProperty("p2", new LongValue.Builder(100).indexed(false).meaning(100).build()) - .build(); + private static final PartialEntity EMBEDDED_ENTITY3 = PartialEntity.builder(INCOMPLETE_KEY1) + .setProperty("p1", STRING_VALUE) + .setProperty("p2", LongValue.builder(100).indexed(false).meaning(100).build()) + .build(); private static final PartialEntityValue EMBEDDED_ENTITY_VALUE1 = new PartialEntityValue(EMBEDDED_ENTITY1); private static final PartialEntityValue EMBEDDED_ENTITY_VALUE2 = new PartialEntityValue(EMBEDDED_ENTITY2); private static final PartialEntityValue EMBEDDED_ENTITY_VALUE3 = new PartialEntityValue(EMBEDDED_ENTITY3); - private static final ListValue LIST_VALUE = - new ListValue.Builder() - .addValue(NULL_VALUE) - .addValue(STRING_VALUE) - .addValue(new NullValue()) - .build(); + private static final ListValue LIST_VALUE = ListValue.builder() + .addValue(NULL_VALUE) + .addValue(STRING_VALUE) + .addValue(new NullValue()) + .build(); @SuppressWarnings("rawtypes") private Multimap typeToValues = ImmutableMultimap.builder() From aab8eefb30684863bbc89bd0789985c23490b9c6 Mon Sep 17 00:00:00 2001 From: ozarov Date: Mon, 8 Dec 2014 22:24:08 -0800 Subject: [PATCH 039/771] Fix tests after builder refactoring. --- .../java/com/google/gcloud/datastore/Key.java | 28 ++++++++- .../google/gcloud/datastore/ListValue.java | 8 ++- .../datastore/DatastoreServiceTest.java | 62 +++++++++++++------ .../gcloud/datastore/SerializationTest.java | 1 + 4 files changed, 78 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index c73f5d8819dc..f569ed811194 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -11,6 +11,7 @@ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; +import java.util.Objects; /** * A key that is guaranteed to be complete and could be used to reference a @@ -76,14 +77,14 @@ protected Key build(String dataset, String namespace, ImmutableList ancestors, String kind, long id) { super(dataset, namespace, ancestors, kind); this.id = id; - this.name = null; + name = null; } public boolean hasId() { @@ -143,6 +144,29 @@ public static Key fromUrlSafe(String urlSafe) { } } + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), name, id); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Key)) { + return false; + } + Key other = (Key) obj; + return Objects.equals(name, other.name) + && Objects.equals(id, other.id) + && super.equals(obj); + } + + @Override + protected void addLeaf(DatastoreV1.Key.Builder keyPb) { + KeyPathElement leaf = + hasId() ? new KeyPathElement(kind(), id) : new KeyPathElement(kind(), name); + keyPb.addPathElement(leaf.toPb()); + } + @Override protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { return fromPb(DatastoreV1.Key.parseFrom(bytesPb)); diff --git a/src/main/java/com/google/gcloud/datastore/ListValue.java b/src/main/java/com/google/gcloud/datastore/ListValue.java index ee829a71dcc0..883b46f0fc28 100644 --- a/src/main/java/com/google/gcloud/datastore/ListValue.java +++ b/src/main/java/com/google/gcloud/datastore/ListValue.java @@ -67,6 +67,12 @@ public Builder addValue(Value first, Value... other) { return this; } + @Override + public Builder indexed(boolean indexed) { + DatastoreServiceException.throwInvalidRequest("ListValue can't specify index"); + return this; + } + /** * Copy the list of values. * @@ -106,6 +112,6 @@ private ListValue(Builder builder) { } public static Builder builder() { - return new Builder().indexed(false); + return new Builder(); } } diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index 970da50d56d5..d65ba937664b 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -40,6 +40,11 @@ public class DatastoreServiceTest { .setProperty("bool", BOOL_VALUE) .setProperty("list", LIST_VALUE1) .build(); + private static final PartialEntity PARTIAL_ENTITY2 = PartialEntity.builder(PARTIAL_ENTITY1) + .removeProperty("str") + .setBooleanProperty("bool", true) + .setListProperty("list", LIST_VALUE1.get()) + .build(); private static final Entity ENTITY1 = Entity.builder(KEY1) .setProperty("str", STR_VALUE) .setProperty("bool", BOOL_VALUE) @@ -49,13 +54,14 @@ public class DatastoreServiceTest { private static final Entity ENTITY2 = Entity.builder(ENTITY1) .key(KEY2) .removeProperty("str") - .setProperty("null", NULL_VALUE) + .setNullProperty("null") .build(); private static final Entity ENTITY3 = Entity.builder(ENTITY1) .key(KEY3) .removeProperty("str") .setProperty("null", NULL_VALUE) - .setProperty("partial2", new PartialEntityValue(ENTITY1)) + .setPartialEntityProperty("partial1", PARTIAL_ENTITY2) + .setPartialEntityProperty("partial2", ENTITY2) .build(); private DatastoreServiceOptions options; @@ -169,23 +175,24 @@ public void testAllocateId() { @Test public void testAllocateIdArray() { KeyBuilder keyBuilder = datastore.newKeyBuilder(KIND1); - PartialKey pKey1 = keyBuilder.build(); - PartialKey pKey2 = keyBuilder.kind(KIND2).addAncestor(KIND1, 10).build(); + PartialKey partialKey1 = keyBuilder.build(); + PartialKey partialKey2 = keyBuilder.kind(KIND2).addAncestor(KIND1, 10).build(); Key key3 = keyBuilder.build("name"); Key key4 = keyBuilder.build(1); - Iterator result = datastore.allocateId(pKey1, pKey2, key3, key4, pKey1, key3); + Iterator result = + datastore.allocateId(partialKey1, partialKey2, key3, key4, partialKey1, key3); Map map = new HashMap<>(); int count = 0; while (result.hasNext()) { map.put(++count, result.next()); } assertEquals(6, map.size()); - assertEquals(pKey1.newKey(map.get(1).id()), map.get(1)); - assertEquals(pKey1.newKey(map.get(5).id()), map.get(5)); - assertEquals(pKey2.newKey(map.get(2).id()), map.get(2)); - assertEquals(key3.builder().id(map.get(3).id()).build(), map.get(3)); - assertEquals(key3.builder().id(map.get(6).id()).build(), map.get(6)); - assertEquals(key4.builder().id(map.get(4).id()).build(), map.get(4)); + assertEquals(partialKey1.newKey(map.get(1).id()), map.get(1)); + assertEquals(partialKey1.newKey(map.get(5).id()), map.get(5)); + assertEquals(partialKey2.newKey(map.get(2).id()), map.get(2)); + assertEquals(Key.builder(key3).id(map.get(3).id()).build(), map.get(3)); + assertEquals(Key.builder(key3).id(map.get(6).id()).build(), map.get(6)); + assertEquals(Key.builder(key4).id(map.get(4).id()).build(), map.get(4)); } @Test @@ -194,24 +201,43 @@ public void testGet() { assertNull(entity); entity = datastore.get(KEY1); + assertEquals(ENTITY1, entity); StringValue value1 = entity.property("str"); BooleanValue value2 = entity.property("bool"); - PartialEntityValue value3 = entity.property("partial1"); + ListValue value3 = entity.property("list"); assertEquals(value1, STR_VALUE); assertEquals(value2, BOOL_VALUE); - assertEquals(value3, new PartialEntityValue(PARTIAL_ENTITY1)); - assertEquals(3, entity.propertyNames().size()); - assertTrue(entity.propertyNames().contains("str")); - assertTrue(entity.propertyNames().contains("bool")); + assertEquals(value3, LIST_VALUE2); + assertEquals(PARTIAL_ENTITY1, entity.partialEntityProperty("partial1")); + assertEquals(4, entity.propertyNames().size()); assertFalse(entity.hasProperty("bla")); } @Test public void testGetArray() { - Iterator result = datastore.get(KEY1, KEY1.builder().name("bla").build(), KEY2); + datastore.put(ENTITY3); + Iterator result = + datastore.get(KEY1, Key.builder(KEY1).name("bla").build(), KEY2, KEY3); assertEquals(ENTITY1, result.next()); assertNull(result.next()); assertEquals(ENTITY2, result.next()); + Entity entity3 = result.next(); + assertEquals(ENTITY3, entity3); + assertTrue(entity3.isNullProperty("null")); + assertEquals(false, entity3.booleanProperty("bool")); + assertEquals(LIST_VALUE2.get(), entity3.listProperty("list")); + PartialEntity partial1 = entity3.partialEntityProperty("partial1"); + Entity partial2 = (Entity) entity3.partialEntityProperty("partial2"); + assertEquals(partial1, PARTIAL_ENTITY2); + assertEquals(partial2, ENTITY2); + assertEquals(Value.Type.BOOLEAN, entity3.propertyType("bool")); + assertEquals(5, entity3.propertyNames().size()); + assertFalse(entity3.hasProperty("bla")); + try { + entity3.stringProperty("str"); + } catch (DatastoreServiceException expected) { + // expected - no such property + } assertFalse(result.hasNext()); } @@ -297,6 +323,6 @@ public void testNewKeyBuilder() { assertEquals(PartialKey.builder(PARTIAL_KEY1).kind(KIND2).build(), datastore.newKeyBuilder(KIND2).build()); assertEquals(KEY1, keyBuilder.build("name")); - assertEquals(KEY1.builder().id(2).build(), keyBuilder.build(2)); + assertEquals(Key.builder(KEY1).id(2).build(), keyBuilder.build(2)); } } diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index a7b7c87baf7f..fc8e0be0d12e 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -96,6 +96,7 @@ public void testTypes() throws Exception { Object[] types = { KEY1, KEY2, INCOMPLETE_KEY1, INCOMPLETE_KEY2, ENTITY1, ENTITY2, ENTITY3, EMBEDDED_ENTITY1, EMBEDDED_ENTITY2, EMBEDDED_ENTITY3}; for (Object obj : types) { + System.out.println("koko->" + obj); Object copy = serialiazeAndDeserialize(obj); assertEquals(obj, obj); assertEquals(obj, copy); From 63c28d442d4077051db18e84e059a7a8c7488e02 Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 9 Dec 2014 12:11:59 -0800 Subject: [PATCH 040/771] work in progress --- .classpath | 3 + .../java/com/google/gcloud/AuthConfig.java | 2 +- .../google/gcloud/datastore/BaseEntity.java | 30 +++------ .../com/google/gcloud/datastore/BaseKey.java | 18 ----- .../com/google/gcloud/datastore/Blob.java | 19 ++++-- .../com/google/gcloud/datastore/Cursor.java | 11 +++- .../google/gcloud/datastore/EntityValue.java | 65 +++++++++++++++++++ .../java/com/google/gcloud/datastore/Key.java | 11 ++-- .../gcloud/datastore/PartialEntity.java | 2 +- .../gcloud/datastore/PartialEntityValue.java | 58 ----------------- .../google/gcloud/datastore/PartialKey.java | 8 --- .../google/gcloud/datastore/Serializable.java | 17 ----- .../datastore/DatastoreServiceTest.java | 12 ++-- .../gcloud/datastore/SerializationTest.java | 14 ++-- 14 files changed, 122 insertions(+), 148 deletions(-) create mode 100644 src/main/java/com/google/gcloud/datastore/EntityValue.java delete mode 100644 src/main/java/com/google/gcloud/datastore/PartialEntityValue.java diff --git a/.classpath b/.classpath index 9ed6ee4d0713..70f23326e0d2 100644 --- a/.classpath +++ b/.classpath @@ -21,6 +21,9 @@ + + +
diff --git a/src/main/java/com/google/gcloud/AuthConfig.java b/src/main/java/com/google/gcloud/AuthConfig.java index 117db5434a00..ddae5ffa1c72 100644 --- a/src/main/java/com/google/gcloud/AuthConfig.java +++ b/src/main/java/com/google/gcloud/AuthConfig.java @@ -8,7 +8,7 @@ import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson.JacksonFactory; -import com.google.api.client.repackaged.com.google.common.base.Preconditions; +import com.google.common.base.Preconditions; import java.io.IOException; import java.security.GeneralSecurityException; diff --git a/src/main/java/com/google/gcloud/datastore/BaseEntity.java b/src/main/java/com/google/gcloud/datastore/BaseEntity.java index 7aae77d360e2..b8a823ca01aa 100644 --- a/src/main/java/com/google/gcloud/datastore/BaseEntity.java +++ b/src/main/java/com/google/gcloud/datastore/BaseEntity.java @@ -76,7 +76,7 @@ public B setBooleanProperty(String name, boolean value) { return self(); } - public B setDateAndTimeProperty(String name, DateTime value) { + public B setDateTimeProperty(String name, DateTime value) { properties.put(name, new DateTimeValue(value)); return self(); } @@ -86,8 +86,8 @@ public B setKeyProperty(String name, Key value) { return self(); } - public B setPartialEntityProperty(String name, PartialEntity value) { - properties.put(name, new PartialEntityValue(value)); + public B setEntityProperty(String name, PartialEntity value) { + properties.put(name, new EntityValue(value)); return self(); } @@ -162,7 +162,7 @@ public boolean booleanProperty(String name) { return ((BooleanValue) property(name)).get(); } - public DateTime dateAndTimeProperty(String name) { + public DateTime dateTimeProperty(String name) { return ((DateTimeValue) property(name)).get(); } @@ -170,8 +170,9 @@ public Key keyProperty(String name) { return ((KeyValue) property(name)).get(); } - public PartialEntity partialEntityProperty(String name) { - return ((PartialEntityValue) property(name)).get(); + @SuppressWarnings("unchecked") + public T entityProperty(String name) { + return (T) ((EntityValue) property(name)).get(); } public List> listProperty(String name) { @@ -183,9 +184,9 @@ public Blob blobProperty(String name) { } /** - * Returns the property's value as {@code RawValue}. + * Returns the property's value as a {@link RawValue}. */ - public RawValue rawValueProperty(String name) { + public RawValue asRawValueProperty(String name) { Value value = property(name); if (value instanceof RawValue) { return (RawValue) value; @@ -201,19 +202,6 @@ public Set propertyNames() { return properties; } - @Override - public int hashCode() { - return properties.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof BaseEntity)) { - return false; - } - return properties.equals(((BaseEntity) obj).properties); - } - @Override protected final DatastoreV1.Entity toPb() { DatastoreV1.Entity.Builder entityPb = DatastoreV1.Entity.newBuilder(); diff --git a/src/main/java/com/google/gcloud/datastore/BaseKey.java b/src/main/java/com/google/gcloud/datastore/BaseKey.java index 47859cd619d4..53d58effc1bc 100644 --- a/src/main/java/com/google/gcloud/datastore/BaseKey.java +++ b/src/main/java/com/google/gcloud/datastore/BaseKey.java @@ -11,7 +11,6 @@ import java.util.LinkedList; import java.util.List; -import java.util.Objects; /** * Base class for keys. @@ -148,23 +147,6 @@ public String kind() { return kind; } - @Override - public int hashCode() { - return Objects.hash(dataset, namespace, ancestors, kind); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof BaseKey)) { - return false; - } - BaseKey other = (BaseKey) obj; - return Objects.equals(dataset, other.dataset) - && Objects.equals(namespace, other.namespace) - && Objects.equals(ancestors, other.ancestors) - && Objects.equals(kind, other.kind); - } - @Override protected DatastoreV1.Key toPb() { DatastoreV1.Key.Builder keyPb = DatastoreV1.Key.newBuilder(); diff --git a/src/main/java/com/google/gcloud/datastore/Blob.java b/src/main/java/com/google/gcloud/datastore/Blob.java index e7e134815236..4edfdabe7ebf 100644 --- a/src/main/java/com/google/gcloud/datastore/Blob.java +++ b/src/main/java/com/google/gcloud/datastore/Blob.java @@ -1,8 +1,10 @@ package com.google.gcloud.datastore; -import static com.google.api.client.util.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.MoreObjects; +import com.google.common.base.MoreObjects.ToStringHelper; import com.google.protobuf.ByteString; import java.io.BufferedInputStream; @@ -10,7 +12,6 @@ import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; -import java.util.Objects; /** * A Google Cloud Datastore Blob. @@ -29,13 +30,21 @@ public final class Blob implements java.io.Serializable { Blob(ByteString byteString, boolean enforceLimits) { this.byteString = checkNotNull(byteString); if (enforceLimits) { - checkArgument(byteString.size() <= MAX_LENGTH, "May be a maximum of 1,000,000 bytes"); + checkArgument(byteString.size() <= MAX_LENGTH, "May be a maximum of %,d bytes", MAX_LENGTH); } } @Override public String toString() { - return byteString.toString(); + ToStringHelper toStringHelper = MoreObjects.toStringHelper(this); + StringBuilder stBuilder = new StringBuilder(); + for (int i = 0; i < Math.min(256, byteString.size()); i++) { + stBuilder.append(String.format("%02x", byteString.byteAt(i))); + } + if (byteString.size() > 256) { + stBuilder.append("..."); + } + return toStringHelper.add("bytes", stBuilder.toString()).toString(); } @Override @@ -48,7 +57,7 @@ public boolean equals(Object obj) { if (!(obj instanceof Blob)) { return false; } - return Objects.equals(byteString, ((Blob) obj).byteString); + return byteString.equals(((Blob) obj).byteString); } /** diff --git a/src/main/java/com/google/gcloud/datastore/Cursor.java b/src/main/java/com/google/gcloud/datastore/Cursor.java index e1163da38150..5b3ba6981af3 100644 --- a/src/main/java/com/google/gcloud/datastore/Cursor.java +++ b/src/main/java/com/google/gcloud/datastore/Cursor.java @@ -1,8 +1,10 @@ package com.google.gcloud.datastore; -import static com.google.api.client.repackaged.com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkNotNull; import static java.nio.charset.StandardCharsets.UTF_8; +import com.google.common.base.MoreObjects; +import com.google.common.base.MoreObjects.ToStringHelper; import com.google.protobuf.ByteString; import java.io.Serializable; @@ -41,7 +43,12 @@ public boolean equals(Object obj) { @Override public String toString() { - return toPb().toString(); + ToStringHelper toStringHelper = MoreObjects.toStringHelper(this); + StringBuilder stBuilder = new StringBuilder(); + for (byte b : bytes) { + stBuilder.append(String.format("%02x", b)); + } + return toStringHelper.add("bytes", stBuilder.toString()).toString(); } /** diff --git a/src/main/java/com/google/gcloud/datastore/EntityValue.java b/src/main/java/com/google/gcloud/datastore/EntityValue.java new file mode 100644 index 000000000000..22c0932cc7af --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/EntityValue.java @@ -0,0 +1,65 @@ +package com.google.gcloud.datastore; + +import static com.google.api.services.datastore.DatastoreV1.Value.ENTITY_VALUE_FIELD_NUMBER; + +import com.google.api.services.datastore.DatastoreV1; + +public class EntityValue extends Value { + + private static final long serialVersionUID = -5461475706792576395L; + + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { + + @Override + public int getProtoFieldId() { + return ENTITY_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(PartialEntity value) { + return builder(value); + } + + @Override + protected PartialEntity getValue(DatastoreV1.Value from) { + return PartialEntity.fromPb(from.getEntityValue()); + } + + @Override + protected void setValue(EntityValue from, DatastoreV1.Value.Builder to) { + to.setEntityValue(from.get().toPb()); + } + }; + + public static final class Builder extends + Value.BaseBuilder { + + private Builder() { + super(Type.ENTITY); + } + + @Override + public Builder indexed(boolean indexed) { + DatastoreServiceException.throwInvalidRequest("EntityValue can't specify index"); + return this; + } + + @Override + public EntityValue build() { + return new EntityValue(this); + } + } + + public EntityValue(PartialEntity entity) { + this(builder(entity)); + } + + private EntityValue(Builder builder) { + super(builder); + } + + public static Builder builder(PartialEntity entity) { + return new Builder().set(entity); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index f569ed811194..d518afca8cdc 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -146,7 +146,7 @@ public static Key fromUrlSafe(String urlSafe) { @Override public int hashCode() { - return Objects.hash(super.hashCode(), name, id); + return Objects.hash(dataset(), namespace(), ancestors(), kind(), name, id); } @Override @@ -155,9 +155,12 @@ public boolean equals(Object obj) { return false; } Key other = (Key) obj; - return Objects.equals(name, other.name) - && Objects.equals(id, other.id) - && super.equals(obj); + return Objects.equals(dataset(), other.dataset()) + && Objects.equals(namespace(), other.namespace()) + && Objects.equals(ancestors(), other.ancestors()) + && Objects.equals(kind(), other.kind()) + && Objects.equals(name, other.name) + && Objects.equals(id, other.id); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java index 0796b2c9f5a1..dfc23c4dc1be 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java @@ -70,7 +70,7 @@ public boolean equals(Object obj) { } PartialEntity other = (PartialEntity) obj; return Objects.equals(key, other.key) - && super.equals(obj); + && Objects.equals(properties(), other.properties()); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java b/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java deleted file mode 100644 index db076a3305a0..000000000000 --- a/src/main/java/com/google/gcloud/datastore/PartialEntityValue.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.google.gcloud.datastore; - -import static com.google.api.services.datastore.DatastoreV1.Value.ENTITY_VALUE_FIELD_NUMBER; - -import com.google.api.services.datastore.DatastoreV1; - -public class PartialEntityValue extends - Value { - - private static final long serialVersionUID = -5461475706792576395L; - - static final BaseMarshaller MARSHALLER = - new BaseMarshaller() { - - @Override - public int getProtoFieldId() { - return ENTITY_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(PartialEntity value) { - return new Builder(value); - } - - @Override - protected PartialEntity getValue(DatastoreV1.Value from) { - return PartialEntity.fromPb(from.getEntityValue()); - } - - @Override - protected void setValue(PartialEntityValue from, DatastoreV1.Value.Builder to) { - to.setEntityValue(from.get().toPb()); - } - }; - - public static final class Builder extends - Value.BaseBuilder { - - public Builder(PartialEntity entity) { - super(Type.PARTIAL_ENTITY); - indexed(false); - set(entity); - } - - @Override - public PartialEntityValue build() { - return new PartialEntityValue(this); - } - } - - public PartialEntityValue(PartialEntity entity) { - this(new Builder(entity)); - } - - PartialEntityValue(Builder builder) { - super(builder); - } -} diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index 129dbdf6e87d..7a27c2fd4b09 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -46,14 +46,6 @@ public Key newKey(long id) { return new Key(dataset(), namespace(), ImmutableList.copyOf(ancestors()), kind(), id); } - @Override - public boolean equals(Object obj) { - if (!(obj instanceof PartialKey)) { - return false; - } - return super.equals(obj); - } - @Override protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { return fromPb(DatastoreV1.Key.parseFrom(bytesPb)); diff --git a/src/main/java/com/google/gcloud/datastore/Serializable.java b/src/main/java/com/google/gcloud/datastore/Serializable.java index e9964b6d0e4a..c9f6b7984512 100644 --- a/src/main/java/com/google/gcloud/datastore/Serializable.java +++ b/src/main/java/com/google/gcloud/datastore/Serializable.java @@ -15,23 +15,6 @@ abstract class Serializable implements java.io.Seria private transient byte[] bytesPb; // only for deserialization - - @Override - public int hashCode() { - return toPb().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!getClass().isInstance(obj)) { - return false; - } - return toPb().equals(((Serializable) obj).toPb()); - } - @Override public String toString() { return toPb().toString(); diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index d65ba937664b..40397e35c47c 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -48,7 +48,7 @@ public class DatastoreServiceTest { private static final Entity ENTITY1 = Entity.builder(KEY1) .setProperty("str", STR_VALUE) .setProperty("bool", BOOL_VALUE) - .setProperty("partial1", new PartialEntityValue(PARTIAL_ENTITY1)) + .setProperty("partial1", new EntityValue(PARTIAL_ENTITY1)) .setProperty("list", LIST_VALUE2) .build(); private static final Entity ENTITY2 = Entity.builder(ENTITY1) @@ -60,8 +60,8 @@ public class DatastoreServiceTest { .key(KEY3) .removeProperty("str") .setProperty("null", NULL_VALUE) - .setPartialEntityProperty("partial1", PARTIAL_ENTITY2) - .setPartialEntityProperty("partial2", ENTITY2) + .setEntityProperty("partial1", PARTIAL_ENTITY2) + .setEntityProperty("partial2", ENTITY2) .build(); private DatastoreServiceOptions options; @@ -208,7 +208,7 @@ public void testGet() { assertEquals(value1, STR_VALUE); assertEquals(value2, BOOL_VALUE); assertEquals(value3, LIST_VALUE2); - assertEquals(PARTIAL_ENTITY1, entity.partialEntityProperty("partial1")); + assertEquals(PARTIAL_ENTITY1, entity.entityProperty("partial1")); assertEquals(4, entity.propertyNames().size()); assertFalse(entity.hasProperty("bla")); } @@ -226,8 +226,8 @@ public void testGetArray() { assertTrue(entity3.isNullProperty("null")); assertEquals(false, entity3.booleanProperty("bool")); assertEquals(LIST_VALUE2.get(), entity3.listProperty("list")); - PartialEntity partial1 = entity3.partialEntityProperty("partial1"); - Entity partial2 = (Entity) entity3.partialEntityProperty("partial2"); + PartialEntity partial1 = entity3.entityProperty("partial1"); + Entity partial2 = (Entity) entity3.entityProperty("partial2"); assertEquals(partial1, PARTIAL_ENTITY2); assertEquals(partial2, ENTITY2); assertEquals(Value.Type.BOOLEAN, entity3.propertyType("bool")); diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index fc8e0be0d12e..96b6d09240b6 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -49,12 +49,12 @@ public class SerializationTest { .setProperty("p1", STRING_VALUE) .setProperty("p2", LongValue.builder(100).indexed(false).meaning(100).build()) .build(); - private static final PartialEntityValue EMBEDDED_ENTITY_VALUE1 = - new PartialEntityValue(EMBEDDED_ENTITY1); - private static final PartialEntityValue EMBEDDED_ENTITY_VALUE2 = - new PartialEntityValue(EMBEDDED_ENTITY2); - private static final PartialEntityValue EMBEDDED_ENTITY_VALUE3 = - new PartialEntityValue(EMBEDDED_ENTITY3); + private static final EntityValue EMBEDDED_ENTITY_VALUE1 = + new EntityValue(EMBEDDED_ENTITY1); + private static final EntityValue EMBEDDED_ENTITY_VALUE2 = + new EntityValue(EMBEDDED_ENTITY2); + private static final EntityValue EMBEDDED_ENTITY_VALUE3 = + new EntityValue(EMBEDDED_ENTITY3); private static final ListValue LIST_VALUE = ListValue.builder() .addValue(NULL_VALUE) .addValue(STRING_VALUE) @@ -66,7 +66,7 @@ public class SerializationTest { .put(Type.NULL, NULL_VALUE) .put(Type.KEY, KEY_VALUE) .put(Type.STRING, STRING_VALUE) - .putAll(Type.PARTIAL_ENTITY, EMBEDDED_ENTITY_VALUE1, EMBEDDED_ENTITY_VALUE2, + .putAll(Type.ENTITY, EMBEDDED_ENTITY_VALUE1, EMBEDDED_ENTITY_VALUE2, EMBEDDED_ENTITY_VALUE3) .put(Type.LIST, LIST_VALUE) .put(Type.LONG, LONG_VALUE) From 28c5d62eb96075a563e859eb6081967f2f04e546 Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 9 Dec 2014 14:18:27 -0800 Subject: [PATCH 041/771] change Value generics --- checkstyle.xml | 165 ++++++++++++++++++ .../google/gcloud/datastore/BaseEntity.java | 24 +-- .../com/google/gcloud/datastore/BaseKey.java | 30 ++-- .../google/gcloud/datastore/BlobValue.java | 7 +- .../google/gcloud/datastore/BooleanValue.java | 7 +- .../gcloud/datastore/DateTimeValue.java | 8 +- .../google/gcloud/datastore/DoubleValue.java | 7 +- .../com/google/gcloud/datastore/Entity.java | 4 +- .../google/gcloud/datastore/EntityValue.java | 14 +- .../java/com/google/gcloud/datastore/Key.java | 36 ++-- .../google/gcloud/datastore/KeyBuilder.java | 2 +- .../com/google/gcloud/datastore/KeyValue.java | 7 +- .../google/gcloud/datastore/ListValue.java | 42 +++-- .../google/gcloud/datastore/LongValue.java | 7 +- .../google/gcloud/datastore/NullValue.java | 7 +- .../gcloud/datastore/PartialEntity.java | 8 +- .../google/gcloud/datastore/PartialKey.java | 44 +++-- .../{KeyPathElement.java => PathElement.java} | 20 +-- .../com/google/gcloud/datastore/RawValue.java | 7 +- .../google/gcloud/datastore/StringValue.java | 7 +- .../com/google/gcloud/datastore/Value.java | 55 +++--- .../gcloud/datastore/SerializationTest.java | 5 +- 22 files changed, 366 insertions(+), 147 deletions(-) create mode 100644 checkstyle.xml rename src/main/java/com/google/gcloud/datastore/{KeyPathElement.java => PathElement.java} (76%) diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 000000000000..a3c9eecc8e46 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/google/gcloud/datastore/BaseEntity.java b/src/main/java/com/google/gcloud/datastore/BaseEntity.java index b8a823ca01aa..41a6674cbcee 100644 --- a/src/main/java/com/google/gcloud/datastore/BaseEntity.java +++ b/src/main/java/com/google/gcloud/datastore/BaseEntity.java @@ -17,11 +17,11 @@ abstract class BaseEntity extends Serializable { private static final long serialVersionUID = 8175618724683792766L; - private final transient ImmutableSortedMap> properties; + private final transient ImmutableSortedMap> properties; protected abstract static class Builder> { - private final Map> properties; + private final Map> properties; protected Builder() { properties = new HashMap<>(); @@ -46,7 +46,7 @@ public B removeProperty(String name) { return self(); } - public B setProperty(String name, Value value) { + public B setProperty(String name, Value value) { properties.put(name, value); return self(); } @@ -91,12 +91,12 @@ public B setEntityProperty(String name, PartialEntity value) { return self(); } - public B setListProperty(String name, List> values) { + public B setListProperty(String name, List> values) { properties.put(name, new ListValue(values)); return self(); } - public B setListProperty(String name, Value... value) { + public B setListProperty(String name, Value... value) { properties.put(name, new ListValue(Arrays.asList(value))); return self(); } @@ -110,10 +110,10 @@ public E build() { return build(ImmutableSortedMap.copyOf(properties)); } - protected abstract E build(ImmutableSortedMap> properties); + protected abstract E build(ImmutableSortedMap> properties); } - protected BaseEntity(ImmutableSortedMap> properties) { + protected BaseEntity(ImmutableSortedMap> properties) { this.properties = properties; } @@ -129,7 +129,7 @@ public boolean hasProperty(String name) { * * @throws DatastoreServiceException if not such property. */ - public > V property(String name) { + public > V property(String name) { @SuppressWarnings("unchecked") V property = (V) properties.get(name); if (property == null) { @@ -175,7 +175,7 @@ public T entityProperty(String name) { return (T) ((EntityValue) property(name)).get(); } - public List> listProperty(String name) { + public List> listProperty(String name) { return ((ListValue) property(name)).get(); } @@ -187,7 +187,7 @@ public Blob blobProperty(String name) { * Returns the property's value as a {@link RawValue}. */ public RawValue asRawValueProperty(String name) { - Value value = property(name); + Value value = property(name); if (value instanceof RawValue) { return (RawValue) value; } @@ -198,14 +198,14 @@ public Set propertyNames() { return properties.keySet(); } - ImmutableSortedMap> properties() { + ImmutableSortedMap> properties() { return properties; } @Override protected final DatastoreV1.Entity toPb() { DatastoreV1.Entity.Builder entityPb = DatastoreV1.Entity.newBuilder(); - for (Map.Entry> entry : properties.entrySet()) { + for (Map.Entry> entry : properties.entrySet()) { DatastoreV1.Property.Builder propertyPb = DatastoreV1.Property.newBuilder(); propertyPb.setName(entry.getKey()); propertyPb.setValue(entry.getValue().toPb()); diff --git a/src/main/java/com/google/gcloud/datastore/BaseKey.java b/src/main/java/com/google/gcloud/datastore/BaseKey.java index 53d58effc1bc..09edf60afeac 100644 --- a/src/main/java/com/google/gcloud/datastore/BaseKey.java +++ b/src/main/java/com/google/gcloud/datastore/BaseKey.java @@ -21,7 +21,7 @@ abstract class BaseKey extends Serializable { private final transient String dataset; private final transient String namespace; - private final transient ImmutableList ancestors; + private final transient ImmutableList ancestors; private final transient String kind; abstract static class Builder> { @@ -29,7 +29,7 @@ abstract static class Builder> { private String dataset; private String namespace; private String kind; - private final List ancestors; + private final List ancestors; private static final int MAX_PATH = 100; @@ -53,26 +53,26 @@ protected B self() { public B addAncestor(String kind, long id) { checkArgument(id != 0, "id must not be equal to zero"); - return addAncestor(new KeyPathElement(kind, id)); + return addAncestor(new PathElement(kind, id)); } public B addAncestor(String kind, String name) { checkArgument(Strings.isNullOrEmpty(name) , "name must not be empty or null"); checkArgument(name.length() <= 500, "name must not exceed 500 characters"); - return addAncestor(new KeyPathElement(kind, name)); + return addAncestor(new PathElement(kind, name)); } - public B addAncestor(KeyPathElement... ancestor) { + public B addAncestor(PathElement... ancestor) { Preconditions.checkState(ancestors.size() + ancestor.length <= MAX_PATH, "path can have at most 100 elements"); - for (KeyPathElement pathElement : ancestor) { + for (PathElement pathElement : ancestor) { ancestors.add(pathElement); } return self(); } - public B addAncestors(Iterable ancestors) { - for (KeyPathElement pathElement : ancestors) { + public B addAncestors(Iterable ancestors) { + for (PathElement pathElement : ancestors) { addAncestor(pathElement); } return self(); @@ -109,10 +109,10 @@ public K build() { } protected abstract K build( - String dataset, String namespace, ImmutableList ancestors, String kind); + String dataset, String namespace, ImmutableList ancestors, String kind); } - BaseKey(String dataset, String namespace, ImmutableList ancestors, String kind) { + BaseKey(String dataset, String namespace, ImmutableList ancestors, String kind) { this.dataset = dataset; this.namespace = namespace; this.ancestors = ancestors; @@ -136,7 +136,7 @@ public String namespace() { /** * Returns an immutable list with the key's ancestors. */ - public List ancestors() { + public List ancestors() { return ancestors; } @@ -160,16 +160,12 @@ protected DatastoreV1.Key toPb() { if (partitionIdPb.hasDatasetId() || partitionIdPb.hasNamespace()) { keyPb.setPartitionId(partitionIdPb.build()); } - for (KeyPathElement pathEntry : ancestors) { + for (PathElement pathEntry : ancestors) { keyPb.addPathElement(pathEntry.toPb()); } addLeaf(keyPb); return keyPb.build(); } - protected void addLeaf(DatastoreV1.Key.Builder keyPb) { - if (kind != null) { - keyPb.addPathElement(new KeyPathElement(kind).toPb()); - } - } + protected abstract void addLeaf(DatastoreV1.Key.Builder keyPb); } diff --git a/src/main/java/com/google/gcloud/datastore/BlobValue.java b/src/main/java/com/google/gcloud/datastore/BlobValue.java index 0b3a3fed9129..8d369c9eb447 100644 --- a/src/main/java/com/google/gcloud/datastore/BlobValue.java +++ b/src/main/java/com/google/gcloud/datastore/BlobValue.java @@ -4,7 +4,7 @@ import com.google.api.services.datastore.DatastoreV1; -public final class BlobValue extends Value { +public final class BlobValue extends Value { private static final long serialVersionUID = -5096238337676649540L; @@ -52,6 +52,11 @@ private BlobValue(Builder builder) { super(builder); } + @Override + public Builder toBuilder() { + return new Builder().mergeFrom(this); + } + public static Builder builder(Blob blob) { return new Builder().set(blob); } diff --git a/src/main/java/com/google/gcloud/datastore/BooleanValue.java b/src/main/java/com/google/gcloud/datastore/BooleanValue.java index 533ee14d5b0d..f6323bb3f794 100644 --- a/src/main/java/com/google/gcloud/datastore/BooleanValue.java +++ b/src/main/java/com/google/gcloud/datastore/BooleanValue.java @@ -4,7 +4,7 @@ import com.google.api.services.datastore.DatastoreV1; -public final class BooleanValue extends Value { +public final class BooleanValue extends Value { private static final long serialVersionUID = -542649497897250340L; @@ -52,6 +52,11 @@ private BooleanValue(Builder builder) { super(builder); } + @Override + public Builder toBuilder() { + return new Builder().mergeFrom(this); + } + public static Builder builder(boolean value) { return new Builder().set(value); } diff --git a/src/main/java/com/google/gcloud/datastore/DateTimeValue.java b/src/main/java/com/google/gcloud/datastore/DateTimeValue.java index 85ac6a853779..a178c71a9665 100644 --- a/src/main/java/com/google/gcloud/datastore/DateTimeValue.java +++ b/src/main/java/com/google/gcloud/datastore/DateTimeValue.java @@ -4,8 +4,7 @@ import com.google.api.services.datastore.DatastoreV1; -public final class DateTimeValue - extends Value { +public final class DateTimeValue extends Value { private static final long serialVersionUID = -5096238337676649540L; @@ -54,6 +53,11 @@ private DateTimeValue(Builder builder) { super(builder); } + @Override + public Builder toBuilder() { + return new Builder().mergeFrom(this); + } + public static Builder builder(DateTime dateTime) { return new Builder().set(dateTime); } diff --git a/src/main/java/com/google/gcloud/datastore/DoubleValue.java b/src/main/java/com/google/gcloud/datastore/DoubleValue.java index b2f4e264c723..749a2a9b7bd5 100644 --- a/src/main/java/com/google/gcloud/datastore/DoubleValue.java +++ b/src/main/java/com/google/gcloud/datastore/DoubleValue.java @@ -4,7 +4,7 @@ import com.google.api.services.datastore.DatastoreV1; -public final class DoubleValue extends Value { +public final class DoubleValue extends Value { private static final long serialVersionUID = -5096238337676649540L; @@ -52,6 +52,11 @@ private DoubleValue(Builder builder) { super(builder); } + @Override + public Builder toBuilder() { + return new Builder().mergeFrom(this); + } + public static Builder builder(double value) { return new Builder().set(value); } diff --git a/src/main/java/com/google/gcloud/datastore/Entity.java b/src/main/java/com/google/gcloud/datastore/Entity.java index f497cb6c7d67..5262fcccd8c1 100644 --- a/src/main/java/com/google/gcloud/datastore/Entity.java +++ b/src/main/java/com/google/gcloud/datastore/Entity.java @@ -39,12 +39,12 @@ public Builder key(Key key) { } @Override - protected Entity build(ImmutableSortedMap> properties) { + protected Entity build(ImmutableSortedMap> properties) { return new Entity(key, properties); } } - Entity(Key key, ImmutableSortedMap> properties) { + Entity(Key key, ImmutableSortedMap> properties) { super(key, properties); } diff --git a/src/main/java/com/google/gcloud/datastore/EntityValue.java b/src/main/java/com/google/gcloud/datastore/EntityValue.java index 22c0932cc7af..3b89d81bb4be 100644 --- a/src/main/java/com/google/gcloud/datastore/EntityValue.java +++ b/src/main/java/com/google/gcloud/datastore/EntityValue.java @@ -3,8 +3,9 @@ import static com.google.api.services.datastore.DatastoreV1.Value.ENTITY_VALUE_FIELD_NUMBER; import com.google.api.services.datastore.DatastoreV1; +import com.google.common.base.Preconditions; -public class EntityValue extends Value { +public class EntityValue extends Value { private static final long serialVersionUID = -5461475706792576395L; @@ -41,8 +42,8 @@ private Builder() { @Override public Builder indexed(boolean indexed) { - DatastoreServiceException.throwInvalidRequest("EntityValue can't specify index"); - return this; + Preconditions.checkArgument(!indexed, "EntityValue can't be indexed"); + return super.indexed(indexed); } @Override @@ -59,7 +60,12 @@ private EntityValue(Builder builder) { super(builder); } + @Override + public Builder toBuilder() { + return new Builder().mergeFrom(this); + } + public static Builder builder(PartialEntity entity) { - return new Builder().set(entity); + return new Builder().set(entity).indexed(false); } } diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index d518afca8cdc..b8a027069a85 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -24,8 +24,7 @@ public final class Key extends PartialKey { private static final long serialVersionUID = 3160994559785491356L; - private final transient String name; - private final transient Long id; + private final transient PathElement leaf; public static final class Builder extends BaseKey.Builder { @@ -64,7 +63,7 @@ public Builder id(long id) { } @Override - protected Key build(String dataset, String namespace, ImmutableList ancestors, + protected Key build(String dataset, String namespace, ImmutableList ancestors, String kind) { if (id == null) { return new Key(dataset, namespace, ancestors, kind, name); @@ -73,47 +72,45 @@ protected Key build(String dataset, String namespace, ImmutableList ancestors, + Key(String dataset, String namespace, ImmutableList ancestors, String kind, String name) { super(dataset, namespace, ancestors, kind); - this.name = name; - id = null; + leaf = new PathElement(kind, name); } - Key(String dataset, String namespace, ImmutableList ancestors, + Key(String dataset, String namespace, ImmutableList ancestors, String kind, long id) { super(dataset, namespace, ancestors, kind); - this.id = id; - name = null; + leaf = new PathElement(kind, id); } public boolean hasId() { - return id != null; + return leaf.hasId(); } /** * Returns the key's id or {@code null} if it has a name instead. */ public Long id() { - return id; + return leaf.id(); } public boolean hasName() { - return name != null; + return leaf.hasName(); } /** * Returns the key's name or {@code null} if it has an id instead. */ public String name() { - return name; + return leaf.name(); } /** * Returns the key's id (as {@link #Long}) or name (as {@link String}). */ public Object nameOrId() { - return hasId() ? id : name; + return leaf.nameOrId(); } /** @@ -146,7 +143,7 @@ public static Key fromUrlSafe(String urlSafe) { @Override public int hashCode() { - return Objects.hash(dataset(), namespace(), ancestors(), kind(), name, id); + return Objects.hash(dataset(), namespace(), ancestors(), kind(), leaf); } @Override @@ -159,14 +156,11 @@ public boolean equals(Object obj) { && Objects.equals(namespace(), other.namespace()) && Objects.equals(ancestors(), other.ancestors()) && Objects.equals(kind(), other.kind()) - && Objects.equals(name, other.name) - && Objects.equals(id, other.id); + && Objects.equals(leaf, other.leaf); } @Override protected void addLeaf(DatastoreV1.Key.Builder keyPb) { - KeyPathElement leaf = - hasId() ? new KeyPathElement(kind(), id) : new KeyPathElement(kind(), name); keyPb.addPathElement(leaf.toPb()); } @@ -209,9 +203,9 @@ private static void addParentToBuilder(Key parent, Builder builder) { builder.namespace(parent.namespace()); builder.addAncestors(parent.ancestors()); if (parent.hasId()) { - builder.addAncestor(new KeyPathElement(parent.kind(), parent.id())); + builder.addAncestor(new PathElement(parent.kind(), parent.id())); } else { - builder.addAncestor(new KeyPathElement(parent.kind(), parent.name())); + builder.addAncestor(new PathElement(parent.kind(), parent.name())); } } } diff --git a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java index 855c09d3358f..6860063124d0 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java +++ b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java @@ -23,7 +23,7 @@ public KeyBuilder(DatastoreService service, String kind) { @Override protected PartialKey build(String dataset, String namespace, - ImmutableList ancestors, String kind) { + ImmutableList ancestors, String kind) { return new PartialKey(dataset, namespace, ancestors, kind); } diff --git a/src/main/java/com/google/gcloud/datastore/KeyValue.java b/src/main/java/com/google/gcloud/datastore/KeyValue.java index 8a6db4ff74a4..b9aab3134a86 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyValue.java +++ b/src/main/java/com/google/gcloud/datastore/KeyValue.java @@ -4,7 +4,7 @@ import com.google.api.services.datastore.DatastoreV1; -public final class KeyValue extends Value { +public final class KeyValue extends Value { private static final long serialVersionUID = -1318353707326704821L; @@ -52,6 +52,11 @@ private KeyValue(Builder builder) { super(builder); } + @Override + public Builder toBuilder() { + return new Builder().mergeFrom(this); + } + public static Builder builder(Key key) { return new Builder().set(key); } diff --git a/src/main/java/com/google/gcloud/datastore/ListValue.java b/src/main/java/com/google/gcloud/datastore/ListValue.java index 883b46f0fc28..cd33c96dcff0 100644 --- a/src/main/java/com/google/gcloud/datastore/ListValue.java +++ b/src/main/java/com/google/gcloud/datastore/ListValue.java @@ -9,13 +9,12 @@ import java.util.ArrayList; import java.util.List; -public final class ListValue extends - Value>, ListValue, ListValue.Builder> { +public final class ListValue extends Value>> { private static final long serialVersionUID = -5461475706792576395L; - static final BaseMarshaller>, ListValue, Builder> MARSHALLER = - new BaseMarshaller>, ListValue, Builder>() { + static final BaseMarshaller>, ListValue, Builder> MARSHALLER = + new BaseMarshaller>, ListValue, Builder>() { @Override public int getProtoFieldId() { @@ -23,13 +22,13 @@ public int getProtoFieldId() { } @Override - public Builder newBuilder(List> values) { + public Builder newBuilder(List> values) { return builder().set(values); } @Override - protected List> getValue(DatastoreV1.Value from) { - List> properties = new ArrayList<>(from.getListValueCount()); + protected List> getValue(DatastoreV1.Value from) { + List> properties = new ArrayList<>(from.getListValueCount()); for (DatastoreV1.Value valuePb : from.getListValueList()) { properties.add(Value.fromPb(valuePb)); } @@ -38,30 +37,30 @@ public Builder newBuilder(List> values) { @Override protected void setValue(ListValue from, DatastoreV1.Value.Builder to) { - for (Value property : from.get()) { + for (Value property : from.get()) { to.addListValue(property.toPb()); } } }; public static final class Builder extends - Value.BaseBuilder>, ListValue, Builder> { + Value.BaseBuilder>, ListValue, Builder> { - private ImmutableList.Builder> listBuilder = ImmutableList.builder(); + private ImmutableList.Builder> listBuilder = ImmutableList.builder(); private Builder() { super(Type.LIST); } - public Builder addValue(Value value) { + public Builder addValue(Value value) { Preconditions.checkArgument(value.type() != Type.LIST, "Cannot contain another list"); listBuilder.add(value); return this; } - public Builder addValue(Value first, Value... other) { + public Builder addValue(Value first, Value... other) { addValue(first); - for (Value value : other) { + for (Value value : other) { addValue(value); } return this; @@ -79,16 +78,16 @@ public Builder indexed(boolean indexed) { * @see com.google.gcloud.datastore.Value.BaseBuilder#set(java.lang.Object) */ @Override - public Builder set(List> properties) { - listBuilder = ImmutableList.>builder(); - for (Value property : properties) { + public Builder set(List> properties) { + listBuilder = ImmutableList.>builder(); + for (Value property : properties) { addValue(property); } return this; } @Override - public List> get() { + public List> get() { return listBuilder.build(); } @@ -99,11 +98,11 @@ public ListValue build() { } } - public ListValue(List> properties) { + public ListValue(List> properties) { this(builder().set(properties)); } - public ListValue(Value first, Value... other) { + public ListValue(Value first, Value... other) { this(new Builder().addValue(first, other)); } @@ -111,6 +110,11 @@ private ListValue(Builder builder) { super(builder); } + @Override + public Builder toBuilder() { + return new Builder().mergeFrom(this); + } + public static Builder builder() { return new Builder(); } diff --git a/src/main/java/com/google/gcloud/datastore/LongValue.java b/src/main/java/com/google/gcloud/datastore/LongValue.java index 97b789e344bd..9107223c7af4 100644 --- a/src/main/java/com/google/gcloud/datastore/LongValue.java +++ b/src/main/java/com/google/gcloud/datastore/LongValue.java @@ -4,7 +4,7 @@ import com.google.api.services.datastore.DatastoreV1; -public final class LongValue extends Value { +public final class LongValue extends Value { private static final long serialVersionUID = -8552854340400546861L; @@ -52,6 +52,11 @@ private LongValue(Builder builder) { super(builder); } + @Override + public Builder toBuilder() { + return new Builder().mergeFrom(this); + } + public static Builder builder(long value) { return new Builder().set(value); } diff --git a/src/main/java/com/google/gcloud/datastore/NullValue.java b/src/main/java/com/google/gcloud/datastore/NullValue.java index bb2c084522ae..cf4f463095d0 100644 --- a/src/main/java/com/google/gcloud/datastore/NullValue.java +++ b/src/main/java/com/google/gcloud/datastore/NullValue.java @@ -4,7 +4,7 @@ import com.google.api.services.datastore.DatastoreV1; -public final class NullValue extends Value { +public final class NullValue extends Value { private static final long serialVersionUID = 8497300779013002270L; @@ -58,6 +58,11 @@ private NullValue(Builder builder) { super(builder); } + @Override + public Builder toBuilder() { + return new Builder().mergeFrom(this); + } + public static Builder builder() { return new Builder(); } diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java index dfc23c4dc1be..ccd754bc0712 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java @@ -37,18 +37,18 @@ public Builder key(PartialKey key) { } @Override - protected PartialEntity build(ImmutableSortedMap> properties) { + protected PartialEntity build(ImmutableSortedMap> properties) { return new PartialEntity(key, properties); } } - protected PartialEntity(PartialKey key, ImmutableSortedMap> properties) { + protected PartialEntity(PartialKey key, ImmutableSortedMap> properties) { super(properties); this.key = key; } public Entity newEntity(Key key) { - return new Entity(key, ImmutableSortedMap.>copyOf(properties())); + return new Entity(key, ImmutableSortedMap.>copyOf(properties())); } /** @@ -86,7 +86,7 @@ protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { } static PartialEntity fromPb(DatastoreV1.Entity entityPb) { - ImmutableSortedMap.Builder> properties = + ImmutableSortedMap.Builder> properties = ImmutableSortedMap.naturalOrder(); for (DatastoreV1.Property property : entityPb.getPropertyList()) { properties.put(property.getName(), Value.fromPb(property.getValue())); diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index 7a27c2fd4b09..e470a71c3376 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -1,11 +1,11 @@ package com.google.gcloud.datastore; import com.google.api.services.datastore.DatastoreV1; -import com.google.api.services.datastore.DatastoreV1.Key.PathElement; import com.google.common.collect.ImmutableList; import com.google.protobuf.InvalidProtocolBufferException; import java.util.List; +import java.util.Objects; /** * A partial key (without a name or id). @@ -28,13 +28,12 @@ private Builder(PartialKey copyFrom) { @Override protected PartialKey build(String dataset, String namespace, - ImmutableList ancestors, String kind) { + ImmutableList ancestors, String kind) { return new PartialKey(dataset, namespace, ancestors, kind); } } - PartialKey(String dataset, String namespace, ImmutableList ancestors, - String kind) { + PartialKey(String dataset, String namespace, ImmutableList ancestors, String kind) { super(dataset, namespace, ancestors, kind); } @@ -46,6 +45,29 @@ public Key newKey(long id) { return new Key(dataset(), namespace(), ImmutableList.copyOf(ancestors()), kind(), id); } + @Override + public int hashCode() { + return Objects.hash(dataset(), namespace(), ancestors(), kind()); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PartialKey) || !PartialKey.class.equals(obj.getClass())) { + return false; + } + + PartialKey other = (PartialKey) obj; + return Objects.equals(dataset(), other.dataset()) + && Objects.equals(namespace(), other.namespace()) + && Objects.equals(ancestors(), other.ancestors()) + && Objects.equals(kind(), other.kind()); + } + + @Override + protected void addLeaf(DatastoreV1.Key.Builder keyPb) { + keyPb.addPathElement(new PathElement(kind()).toPb()); + } + @Override protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { return fromPb(DatastoreV1.Key.parseFrom(bytesPb)); @@ -63,15 +85,15 @@ static PartialKey fromPb(DatastoreV1.Key keyPb) { namespace = partitionIdPb.getNamespace(); } } - List pathElementsPb = keyPb.getPathElementList(); + List pathElementsPb = keyPb.getPathElementList(); if (pathElementsPb.isEmpty()) { - return new PartialKey(dataset, namespace, ImmutableList.of(), null); + return new PartialKey(dataset, namespace, ImmutableList.of(), null); } - ImmutableList.Builder pathBuilder = ImmutableList.builder(); + ImmutableList.Builder pathBuilder = ImmutableList.builder(); for (int i = 0; i < pathElementsPb.size() - 1; i++) { - pathBuilder.add(KeyPathElement.fromPb(pathElementsPb.get(i))); + pathBuilder.add(PathElement.fromPb(pathElementsPb.get(i))); } - PathElement leaf = pathElementsPb.get(pathElementsPb.size() - 1); + DatastoreV1.Key.PathElement leaf = pathElementsPb.get(pathElementsPb.size() - 1); String kind = leaf.getKind(); if (leaf.hasId()) { return new Key(dataset, namespace, pathBuilder.build(), kind, leaf.getId()); @@ -94,9 +116,9 @@ public static Builder builder(Key parent, String kind) { .namespace(parent.namespace()) .addAncestors(parent.ancestors()); if (parent.hasId()) { - builder.addAncestor(new KeyPathElement(parent.kind(), parent.id())); + builder.addAncestor(new PathElement(parent.kind(), parent.id())); } else { - builder.addAncestor(new KeyPathElement(parent.kind(), parent.name())); + builder.addAncestor(new PathElement(parent.kind(), parent.name())); } return builder; } diff --git a/src/main/java/com/google/gcloud/datastore/KeyPathElement.java b/src/main/java/com/google/gcloud/datastore/PathElement.java similarity index 76% rename from src/main/java/com/google/gcloud/datastore/KeyPathElement.java rename to src/main/java/com/google/gcloud/datastore/PathElement.java index 78b529ed6e7b..213c5a7bdae8 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyPathElement.java +++ b/src/main/java/com/google/gcloud/datastore/PathElement.java @@ -8,7 +8,7 @@ /** * Represents a single element in a key's path. */ -public final class KeyPathElement extends Serializable { +public final class PathElement extends Serializable { private static final long serialVersionUID = -7968078857690784595L; @@ -16,17 +16,17 @@ public final class KeyPathElement extends Serializable { +public final class RawValue extends Value { private static final long serialVersionUID = -3359604598651897941L; @@ -50,6 +50,11 @@ private RawValue(Builder builder) { this(builder(valuePb)); } + @Override + public Builder toBuilder() { + return new Builder().mergeFrom(this); + } + static Builder builder(DatastoreV1.Value valuePb) { Builder builder = new Builder(); if (valuePb.hasIndexed()) { diff --git a/src/main/java/com/google/gcloud/datastore/StringValue.java b/src/main/java/com/google/gcloud/datastore/StringValue.java index 4d35aa3e3e01..6434b6a86f40 100644 --- a/src/main/java/com/google/gcloud/datastore/StringValue.java +++ b/src/main/java/com/google/gcloud/datastore/StringValue.java @@ -5,7 +5,7 @@ import com.google.api.services.datastore.DatastoreV1; -public final class StringValue extends Value { +public final class StringValue extends Value { private static final long serialVersionUID = -3105699707394545523L; @@ -56,6 +56,11 @@ private StringValue(Builder builder) { super(builder); } + @Override + public Builder toBuilder() { + return new Builder().mergeFrom(this); + } + public static Builder builder(String value) { return new Builder().set(value); } diff --git a/src/main/java/com/google/gcloud/datastore/Value.java b/src/main/java/com/google/gcloud/datastore/Value.java index d35820e38db7..448efe37ed31 100644 --- a/src/main/java/com/google/gcloud/datastore/Value.java +++ b/src/main/java/com/google/gcloud/datastore/Value.java @@ -4,7 +4,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.api.services.datastore.DatastoreV1; -import com.google.common.base.MoreObjects; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.InvalidProtocolBufferException; @@ -23,9 +22,7 @@ * @param

the type of this value * @param the type of this value's builder */ -public abstract class - Value, B extends Value.Builder> - extends Serializable { +public abstract class Value extends Serializable { private static final long serialVersionUID = -1899638277588872742L; private static final Map DESCRIPTOR_TO_TYPE_MAP = new HashMap<>(); @@ -53,9 +50,9 @@ public enum Type { STRING(StringValue.MARSHALLER, StringValue.MARSHALLER), /** - * Represents an {@link PartialEntity} value. + * Represents an entity ({@link PartialEntity} or {@link Entity}) value. */ - PARTIAL_ENTITY(PartialEntityValue.MARSHALLER, PartialEntityValue.MARSHALLER), + ENTITY(EntityValue.MARSHALLER, EntityValue.MARSHALLER), /** * Represents a {@code list} of {@link Value}s. @@ -101,8 +98,8 @@ public enum Type { @SuppressWarnings("rawtypes") private final BuilderFactory builderFactory; @SuppressWarnings("rawtypes") private final Marshaller marshaller; - , B extends Builder> - Type(Marshaller marshaller, BuilderFactory builderFactory) { + , B extends Builder> Type(Marshaller marshaller, + BuilderFactory builderFactory) { this.marshaller = marshaller; this.builderFactory = builderFactory; int fieldId = marshaller.getProtoFieldId(); @@ -111,23 +108,22 @@ public enum Type { } } - , B extends Builder> Marshaller - getMarshaller() { + , B extends Builder> Marshaller getMarshaller() { return marshaller; } - , B extends Builder> BuilderFactory + , B extends Builder> BuilderFactory getBuilderFactory() { return builderFactory; } } - interface BuilderFactory, B extends Builder> { + interface BuilderFactory, B extends Builder> { B newBuilder(V value); } - interface Builder, B extends Builder> { + interface Builder, B extends Builder> { Type getType(); @@ -148,7 +144,7 @@ interface Builder, B extends Builder> { P build(); } - interface Marshaller, B extends Builder> { + interface Marshaller, B extends Builder> { B fromProto(DatastoreV1.Value proto); @@ -157,7 +153,7 @@ interface Marshaller, B extends Builder> { int getProtoFieldId(); } - abstract static class BaseMarshaller, B extends Builder> + abstract static class BaseMarshaller, B extends Builder> implements Marshaller, BuilderFactory { @Override @@ -190,7 +186,7 @@ public final DatastoreV1.Value toProto(P value) { protected abstract void setValue(P from, DatastoreV1.Value.Builder to); } - abstract static class BaseBuilder, B extends BaseBuilder> + abstract static class BaseBuilder, B extends BaseBuilder> implements Builder { private final Type type; @@ -257,7 +253,7 @@ private B self() { public abstract P build(); } - Value(Builder builder) { +

, B extends BaseBuilder> Value(Builder builder) { type = builder.getType(); indexed = builder.getIndexed(); meaning = builder.getMeaning(); @@ -280,9 +276,8 @@ public final boolean hasIndexed() { return indexed != null; } - public final boolean indexed() { - // default indexed value is true - return MoreObjects.firstNonNull(indexed, Boolean.TRUE); + public final Boolean indexed() { + return indexed; } public final boolean hasMeaning() { @@ -297,20 +292,15 @@ public final V get() { return value; } - @SuppressWarnings("unchecked") - public final B toBuilder() { - BuilderFactory builderFactory = type().getBuilderFactory(); - B builder = builderFactory.newBuilder(get()); - return builder.mergeFrom((P) this); - } + public abstract Builder toBuilder(); @Override public int hashCode() { - return Objects.hash(type, indexed, meaning); + return Objects.hash(type, indexed, meaning, value); } - @SuppressWarnings("unchecked") @Override + @SuppressWarnings("unchecked") public boolean equals(Object obj) { if (obj == this) { return true; @@ -318,7 +308,7 @@ public boolean equals(Object obj) { if (!getClass().isInstance(obj)) { return false; } - Value other = (Value) obj; + Value other = (Value) obj; return Objects.equals(type, other.type) && Objects.equals(indexed, other.indexed) && Objects.equals(meaning, other.meaning) @@ -326,13 +316,12 @@ public boolean equals(Object obj) { } @Override - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) protected DatastoreV1.Value toPb() { - Marshaller marshaller = type().getMarshaller(); - return marshaller.toProto((P) this); + return type().getMarshaller().toProto((Value) this); } - static Value fromPb(DatastoreV1.Value proto) { + static Value fromPb(DatastoreV1.Value proto) { for (Entry entry : proto.getAllFields().entrySet()) { FieldDescriptor descriptor = entry.getKey(); if (descriptor.getName().endsWith("_value")) { diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 96b6d09240b6..92ad4a672460 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -80,8 +80,8 @@ public class SerializationTest { @Test public void testValues() throws Exception { for (Type type : Type.values()) { - for (Value value : typeToValues.get(type)) { - Value copy = serialiazeAndDeserialize(value); + for (Value value : typeToValues.get(type)) { + Value copy = serialiazeAndDeserialize(value); assertEquals(value, value); assertEquals(value, copy); assertNotSame(value, copy); @@ -96,7 +96,6 @@ public void testTypes() throws Exception { Object[] types = { KEY1, KEY2, INCOMPLETE_KEY1, INCOMPLETE_KEY2, ENTITY1, ENTITY2, ENTITY3, EMBEDDED_ENTITY1, EMBEDDED_ENTITY2, EMBEDDED_ENTITY3}; for (Object obj : types) { - System.out.println("koko->" + obj); Object copy = serialiazeAndDeserialize(obj); assertEquals(obj, obj); assertEquals(obj, copy); From baf41a7b3a2b69d6762e8c76ccd788cb82f3466d Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 9 Dec 2014 14:23:53 -0800 Subject: [PATCH 042/771] fix package-info.java --- src/main/java/com/google/gcloud/datastore/package-info.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/package-info.java b/src/main/java/com/google/gcloud/datastore/package-info.java index 60aaddb13a74..5b2b8b8e8db5 100644 --- a/src/main/java/com/google/gcloud/datastore/package-info.java +++ b/src/main/java/com/google/gcloud/datastore/package-info.java @@ -16,11 +16,11 @@ * datastore.put(entity); * } else { * boolean updated = entity.booleanProperty("updated"); - * if (!updated.get) { + * if (!updated) { * String[] name = entity.stringProperty("name").split(" "); - * entity = entity.builder() + * entity = Entity.builder(entity) * .setStringProperty("name", name[0]) - * .setProperty("last_name", StringProperty.builder(name[1]).indexed(false).build()) + * .setProperty("last_name", StringValue.builder(name[1]).indexed(false).build()) * .setBooleanProperty("updated", true) * .removeProperty("old_property") * .setDoubleProperty("new_property", 1.1) From 546cd049cbde735c760988310089dc074a73cc8e Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 9 Dec 2014 16:40:17 -0800 Subject: [PATCH 043/771] update error handling --- .classpath | 13 +- pom.xml | 5 + .../datastore/DatastoreServiceException.java | 77 ++++++---- .../gcloud/datastore/TransactionImpl.java | 4 +- .../datastore/DatastoreServiceTest.java | 132 +++++++++++++++++- 5 files changed, 193 insertions(+), 38 deletions(-) diff --git a/.classpath b/.classpath index 70f23326e0d2..d6cf6121af66 100644 --- a/.classpath +++ b/.classpath @@ -21,10 +21,17 @@ - - - + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 478c89fd38d0..d3431c1584fd 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,11 @@ joda-time RELEASE + + org.json + json + 20090211 + diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java index 94c16966f1fb..7e5ec384c65c 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java @@ -1,16 +1,17 @@ package com.google.gcloud.datastore; -import static com.google.common.base.MoreObjects.firstNonNull; - import com.google.api.services.datastore.client.DatastoreException; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableMap; -import java.util.HashMap; -import java.util.Map; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; public class DatastoreServiceException extends RuntimeException { private static final long serialVersionUID = 8170357898917041899L; - private static final Map HTTP_TO_CODE = new HashMap<>(); + private static final ImmutableMap REASON_TO_CODE; private final Code code; @@ -21,23 +22,22 @@ public class DatastoreServiceException extends RuntimeException { */ public enum Code { - ABORTED(409, true, "Request aborted"), - DEADLINE_EXCEEDED(403, true, "Deadline exceeded"), - UNAVAILABLE(503, true, "Could not reach service"), - FAILED_PRECONDITION(412, false, "Invalid request"), - INVALID_ARGUMENT(400, false, "Request parameter has an invalid value"), - PERMISSION_DENIED(403, false, "Unauthorized request"), - RESOURCE_EXHAUSTED(402, false, "Quota exceeded"), - INTERNAL(500, false, "Server returned an error"), - UNKNOWN(0, false, "Unknown failure"); + ABORTED(true, "Request aborted"), + DEADLINE_EXCEEDED(true, "Deadline exceeded"), + UNAVAILABLE(true, "Could not reach service"), + FAILED_PRECONDITION(false, "Invalid request"), + INVALID_ARGUMENT(false, "Request parameter has an invalid value"), + PERMISSION_DENIED(false, "Unauthorized request"), + RESOURCE_EXHAUSTED(false, "Quota exceeded"), + INTERNAL(false, "Server returned an error"), + UNKNOWN(false, "Unknown failure"); private final boolean isTransient; - private final String msg; + private final String defaultMessage; - Code(int httpStatus, boolean isTransient, String msg) { + Code(boolean isTransient, String msg) { this.isTransient = isTransient; - this.msg = msg; - HTTP_TO_CODE.put(httpStatus, this); + this.defaultMessage = msg; } /** @@ -48,16 +48,28 @@ public boolean isTransient() { return isTransient; } - DatastoreServiceException translate(DatastoreException exception) { - return new DatastoreServiceException(this, exception); + DatastoreServiceException translate(DatastoreException exception, String msg) { + return new DatastoreServiceException(this, msg, exception); } } - public DatastoreServiceException(Code code, Exception cause) { - super(code.msg, cause); + static { + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (Code code : Code.values()) { + builder.put(code.name(), code); + } + REASON_TO_CODE = builder.build(); + } + + public DatastoreServiceException(Code code, String msg, Exception cause) { + super(MoreObjects.firstNonNull(msg, code.defaultMessage), cause); this.code = code; } + public DatastoreServiceException(Code code, String msg) { + this(code, msg, null); + } + /** * Returns the code associated with this exception. */ @@ -72,9 +84,23 @@ public Code code() { * @throws DatastoreServiceException every time */ static DatastoreServiceException translateAndThrow(DatastoreException exception) { - throw firstNonNull(HTTP_TO_CODE.get(exception.getCode()), Code.UNKNOWN).translate(exception); + String message = exception.getMessage(); + String reason = ""; + if (message != null) { + try { + JSONObject json = new JSONObject(new JSONTokener(exception.getMessage())); + JSONObject error = json.getJSONObject("error").getJSONArray("errors").getJSONObject(0); + reason = error.getString("reason"); + message = error.getString("message"); + } catch (JSONException ex) { + // ignore - will be converted to unknown + } + } + Code code = MoreObjects.firstNonNull(REASON_TO_CODE.get(reason), Code.UNKNOWN); + throw code.translate(exception, message); } + /** * Throw a DatastoreServiceException with {@code FAILED_PRECONDITION} code and the {@code msg} * in a nested exception. @@ -82,9 +108,6 @@ static DatastoreServiceException translateAndThrow(DatastoreException exception) * @throws DatastoreServiceException every time */ static DatastoreServiceException throwInvalidRequest(String msg, Object... params) { - if (params.length > 0) { - msg = String.format(msg, params); - } - throw new DatastoreServiceException(Code.FAILED_PRECONDITION, new RuntimeException(msg)); + throw new DatastoreServiceException(Code.FAILED_PRECONDITION, String.format(msg, params)); } } diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java index 17fe3e1339fc..0b6e4e81b24c 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java @@ -55,7 +55,9 @@ public void commit() { @Override public void rollback() { super.checkValid(); - datastore.rollbackTransaction(transaction); + if (!wasRolledback) { + datastore.rollbackTransaction(transaction); + } wasRolledback = true; } diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index 40397e35c47c..23567330bbc8 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -29,6 +29,8 @@ public class DatastoreServiceTest { private static final Key KEY1 = PARTIAL_KEY1.newKey("name"); private static final Key KEY2 = Key.builder(KEY1, KIND2, 1).build(); private static final Key KEY3 = Key.builder(KEY2).name("bla").build(); + private static final Key KEY4 = KEY2.newKey("newName1"); + private static final Key KEY5 = KEY2.newKey("newName2"); private static final KeyValue KEY_VALUE = new KeyValue(KEY1); private static final ListValue LIST_VALUE1 = ListValue.builder() .addValue(NULL_VALUE) @@ -80,7 +82,7 @@ public void setUp() { .build(); datastore = DatastoreServiceFactory.getDefault(options); // Prepare data for testing - datastore.delete(KEY1, KEY2, KEY3); + datastore.delete(KEY1, KEY2, KEY3, KEY4, KEY5); datastore.add(ENTITY1, ENTITY2); } @@ -91,13 +93,119 @@ public void testGetOptions() { @Test public void testNewTransactionCommit() { - // TODO (also try list value with different types) - fail("Not yet implemented"); + Transaction transaction = datastore.newTransaction(); + transaction.add(ENTITY3); + Entity entity2 = Entity.builder(ENTITY2) + .clearProperties() + .setNullProperty("bla") + .build(); + transaction.update(entity2); + transaction.delete(KEY1); + transaction.commit(); + + Iterator iter = datastore.get(KEY1, KEY2, KEY3); + assertNull(iter.next()); + assertEquals(entity2, iter.next()); + assertEquals(ENTITY3, iter.next()); + assertFalse(iter.hasNext()); + + try { + transaction.commit(); + fail("Expecting a failure"); + } catch (DatastoreServiceException ex) { + // expected to fail + } + + try { + transaction.rollback(); + fail("Expecting a failure"); + } catch (DatastoreServiceException ex) { + // expected to fail + } + + verifyNotUsable(transaction); + } + + @Test + public void testTransactionWithRead() { + Transaction transaction = datastore.newTransaction(); + assertNull(transaction.get(KEY3)); + transaction.add(ENTITY3); + transaction.commit(); + assertEquals(ENTITY3, datastore.get(KEY3)); + + transaction = datastore.newTransaction(); + assertEquals(ENTITY3, transaction.get(KEY3)); + // update entity3 during the transaction + datastore.put(Entity.builder(ENTITY3).clearProperties().build()); + transaction.update(ENTITY2); + try { + transaction.commit(); + fail("Expecting a failure"); + } catch (DatastoreServiceException expected) { + expected.printStackTrace(); + assertEquals(DatastoreServiceException.Code.ABORTED, expected.code()); + } } @Test public void testNewTransactionRollback() { - fail("Not yet implemented"); + Transaction transaction = datastore.newTransaction(); + transaction.add(ENTITY3); + Entity entity2 = Entity.builder(ENTITY2) + .clearProperties() + .setNullProperty("bla") + .setListProperty("list3", new StringValue("bla"), StringValue.builder("bla").build()) + .build(); + transaction.update(entity2); + transaction.delete(KEY1); + transaction.rollback(); + transaction.rollback(); // should be safe to repeat rollback calls + + try { + transaction.commit(); + fail("Expecting a failure"); + } catch (DatastoreServiceException ex) { + // expected to fail + } + + verifyNotUsable(transaction); + + Iterator iter = datastore.get(KEY1, KEY2, KEY3); + assertEquals(ENTITY1, iter.next()); + assertEquals(ENTITY2, iter.next()); + assertNull(iter.next()); + assertFalse(iter.hasNext()); + } + + private void verifyNotUsable(DatastoreWriter writer) { + try { + writer.add(ENTITY3); + fail("Expecting a failure"); + } catch (DatastoreServiceException ex) { + // expected to fail + } + + try { + writer.put(ENTITY3); + fail("Expecting a failure"); + } catch (DatastoreServiceException ex) { + // expected to fail + } + + try { + writer.update(ENTITY3); + fail("Expecting a failure"); + } catch (DatastoreServiceException ex) { + // expected to fail + } + + try { + writer.delete(ENTITY3.key()); + fail("Expecting a failure"); + } catch (DatastoreServiceException ex) { + // expected to fail + } } @Test @@ -108,10 +216,10 @@ public void testNewBatchWriter() { .clearProperties() .setNullProperty("bla") .build(); - Entity entity4 = Entity.builder(KEY2.newKey("newName1")) + Entity entity4 = Entity.builder(KEY4) .setProperty("value", new StringValue("value")) .build(); - Entity entity5 = Entity.builder(KEY2.newKey("newName2")) + Entity entity5 = Entity.builder(KEY5) .setStringProperty("value", "value") .build(); batchWriter.add(entity4, entity5); @@ -127,9 +235,12 @@ public void testNewBatchWriter() { try { batchWriter.submit(); + fail("Expecting a failure"); } catch (DatastoreServiceException ex) { // expected to fail } + verifyNotUsable(batchWriter); + batchWriter = datastore.newBatchWriter(); batchWriter.delete(entity4.key(), entity5.key()); batchWriter.update(ENTITY1, ENTITY2, ENTITY3); @@ -142,7 +253,11 @@ public void testNewBatchWriter() { assertNull(entities.next()); assertFalse(entities.hasNext()); - // TODO need to cover more edge cases (ds failures, re-use of same entities,..) + // TODO need to cover the cases of: + // delete after put/add/update + // put after delete/add/update + // update after delete/add/put + // add after delete/update/put } @Test @@ -235,6 +350,7 @@ public void testGetArray() { assertFalse(entity3.hasProperty("bla")); try { entity3.stringProperty("str"); + fail("Expecting a failure"); } catch (DatastoreServiceException expected) { // expected - no such property } @@ -250,6 +366,7 @@ public void testAdd() { try { datastore.add(ENTITY1); + fail("Expecting a failure"); } catch (DatastoreServiceException expected) { // expected; } @@ -266,6 +383,7 @@ public void testUpdate() { try { datastore.update(ENTITY3); + fail("Expecting a failure"); } catch (DatastoreServiceException expected) { // expected; } From 5474d4f60576ced7a2584212a41e876e38366a1b Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 9 Dec 2014 17:42:36 -0800 Subject: [PATCH 044/771] query impl - work in progress --- .../google/gcloud/datastore/QueryResult.java | 26 ++++++-- .../gcloud/datastore/QueryResultImpl.java | 64 +++++++++++++++++++ 2 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/google/gcloud/datastore/QueryResultImpl.java diff --git a/src/main/java/com/google/gcloud/datastore/QueryResult.java b/src/main/java/com/google/gcloud/datastore/QueryResult.java index 446fc6dc43e4..8029c2654ce3 100644 --- a/src/main/java/com/google/gcloud/datastore/QueryResult.java +++ b/src/main/java/com/google/gcloud/datastore/QueryResult.java @@ -1,5 +1,7 @@ package com.google.gcloud.datastore; +import com.google.api.services.datastore.DatastoreV1; + import java.util.Iterator; /** @@ -19,9 +21,23 @@ public interface QueryResult extends Iterator { */ enum Type { - FULL(Entity.class), - PROJECTION(PartialEntity.class), - KEY_ONLY(Key.class); + FULL(Entity.class) { + @Override Object convert(DatastoreV1.Entity value) { + return Entity.fromPb(value); + } + }, + + PROJECTION(PartialEntity.class) { + @Override Object convert(DatastoreV1.Entity value) { + return PartialEntity.fromPb(value); + } + }, + + KEY_ONLY(Key.class) { + @Override Object convert(DatastoreV1.Entity value) { + return Key.fromPb(value.getKey()); + } + }; private final Class resultClass; @@ -29,9 +45,11 @@ enum Type { this.resultClass = resultClass; } - Class getResultClass() { + public Class getResultClass() { return resultClass; } + + abstract Object convert(DatastoreV1.Entity value); } /** diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java new file mode 100644 index 000000000000..aa237944ca42 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java @@ -0,0 +1,64 @@ +package com.google.gcloud.datastore; + +import com.google.api.services.datastore.DatastoreV1; +import com.google.api.services.datastore.DatastoreV1.EntityResult; +import com.google.common.collect.ImmutableMap; + +import java.util.Iterator; + +public class QueryResultImpl implements QueryResult { + + private static final ImmutableMap + RESULT_TYPE_CONVERTER; + + private final QueryResult.Type type; + private Iterator entityResultPbIter; + private boolean moreResult; + + static { + ImmutableMap.Builder builder = + ImmutableMap.builder(); + for (DatastoreV1.EntityResult.ResultType type : DatastoreV1.EntityResult.ResultType.values()) { + builder.put(type, QueryResult.Type.valueOf(type.name())); + } + RESULT_TYPE_CONVERTER = builder.build(); + } + + QueryResultImpl(DatastoreV1.Query DatastoreV1.QueryResultBatch resultBatch) { + type = RESULT_TYPE_CONVERTER.get(resultBatch.getEntityResultType()); + entityResultPbIter = resultBatch.getEntityResultList().iterator(); + moreResult = + DatastoreV1.QueryResultBatch.MoreResultsType.NOT_FINISHED == resultBatch.getMoreResults(); + } + + @Override + public boolean hasNext() { + if (entityResultPbIter.hasNext()) { + return true; + } else if (moreResult) { + // need to fetch more and update results (and more results) + } + return false; + } + + @Override + public V next() { + return (V) type.convert(entityResultPbIter.next()); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("QueryResult is read-only"); + } + + @Override + public QueryResult.Type getType() { + return type; + } + + @Override + public Cursor getCursor() { + // TODO(ozarov): implement when v1beta3 is available + return null; + } +} From 7630e70cd92900fa2958e4b123fce6edbb6a8ddf Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 10 Dec 2014 16:30:47 -0800 Subject: [PATCH 045/771] added Gql --- .../google/gcloud/datastore/BatchWriter.java | 5 + .../com/google/gcloud/datastore/Blob.java | 20 +- .../com/google/gcloud/datastore/Cursor.java | 58 +-- .../gcloud/datastore/DatastoreReader.java | 2 +- .../datastore/DatastoreServiceImpl.java | 26 +- .../com/google/gcloud/datastore/DateTime.java | 21 +- .../google/gcloud/datastore/EntityValue.java | 1 + .../com/google/gcloud/datastore/GqlQuery.java | 329 +++++++++++++++++- .../java/com/google/gcloud/datastore/Key.java | 3 + .../google/gcloud/datastore/ListValue.java | 11 +- .../gcloud/datastore/PartialEntity.java | 3 + .../google/gcloud/datastore/PartialKey.java | 3 + .../google/gcloud/datastore/PathElement.java | 3 + .../com/google/gcloud/datastore/Query.java | 40 ++- .../google/gcloud/datastore/QueryResult.java | 13 +- .../gcloud/datastore/QueryResultImpl.java | 51 ++- .../google/gcloud/datastore/Transaction.java | 9 +- .../gcloud/datastore/TransactionImpl.java | 2 +- .../gcloud/datastore/SerializationTest.java | 18 +- 19 files changed, 544 insertions(+), 74 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriter.java b/src/main/java/com/google/gcloud/datastore/BatchWriter.java index 889ff425a1ab..22fe2ded6179 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchWriter.java +++ b/src/main/java/com/google/gcloud/datastore/BatchWriter.java @@ -1,5 +1,10 @@ package com.google.gcloud.datastore; +/** + * An interface to represent a batch of write operations. + * Any write operation that is applied on a batch will only be sent + * to the Datastore upon {@link #submit} and with as few RPC calls as possible. + */ public interface BatchWriter extends DatastoreWriter { /** diff --git a/src/main/java/com/google/gcloud/datastore/Blob.java b/src/main/java/com/google/gcloud/datastore/Blob.java index 4edfdabe7ebf..a176a10e25c3 100644 --- a/src/main/java/com/google/gcloud/datastore/Blob.java +++ b/src/main/java/com/google/gcloud/datastore/Blob.java @@ -3,9 +3,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.api.services.datastore.DatastoreV1; +import com.google.api.services.datastore.DatastoreV1.Value; import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; @@ -20,12 +23,12 @@ * * @see Google Cloud Datastore Entities, Properties, and Keys */ -public final class Blob implements java.io.Serializable { +public final class Blob extends Serializable { private static final long serialVersionUID = 3835421019618247721L; private static final int MAX_LENGTH = 1_000_000; - private final ByteString byteString; + private final transient ByteString byteString; Blob(ByteString byteString, boolean enforceLimits) { this.byteString = checkNotNull(byteString); @@ -54,6 +57,9 @@ public int hashCode() { @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } if (!(obj instanceof Blob)) { return false; } @@ -134,4 +140,14 @@ public static Blob copyFrom(InputStream input) throws IOException { } return copyFrom(bytes.toByteArray()); } + + @Override + protected Value toPb() { + return DatastoreV1.Value.newBuilder().setBlobValue(byteString).build(); + } + + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + return new Blob(DatastoreV1.Value.parseFrom(bytesPb).getBlobValue(), false); + } } diff --git a/src/main/java/com/google/gcloud/datastore/Cursor.java b/src/main/java/com/google/gcloud/datastore/Cursor.java index 5b3ba6981af3..e95f25144c12 100644 --- a/src/main/java/com/google/gcloud/datastore/Cursor.java +++ b/src/main/java/com/google/gcloud/datastore/Cursor.java @@ -3,83 +3,99 @@ import static com.google.common.base.Preconditions.checkNotNull; import static java.nio.charset.StandardCharsets.UTF_8; +import com.google.api.services.datastore.DatastoreV1; +import com.google.api.services.datastore.DatastoreV1.Value; import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; -import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; -import java.util.Arrays; /** * A Google Cloud Datastore cursor. * The cursor can be used to as a starting point or an ending point for a {@link Query} */ -public final class Cursor implements Serializable { +public final class Cursor extends Serializable { private static final long serialVersionUID = -1423744878777486541L; - private final byte[] bytes; + private final transient ByteString byteString; - public Cursor(byte[] bytes) { - this.bytes = checkNotNull(bytes); + Cursor(ByteString byteString) { + this.byteString = byteString; } @Override public int hashCode() { - return Arrays.hashCode(bytes); + return byteString.hashCode(); } @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } if (!(obj instanceof Cursor)) { return false; } - - return Arrays.equals(bytes, ((Cursor) obj).bytes); + return byteString.equals(((Cursor) obj).byteString); } @Override public String toString() { ToStringHelper toStringHelper = MoreObjects.toStringHelper(this); StringBuilder stBuilder = new StringBuilder(); - for (byte b : bytes) { - stBuilder.append(String.format("%02x", b)); + for (int i = 0; i < byteString.size(); i++) { + stBuilder.append(String.format("%02x", byteString.byteAt(i))); } return toStringHelper.add("bytes", stBuilder.toString()).toString(); } + ByteString byteString() { + return byteString; + } + /** * Returns the cursor in an encoded form that can be used as part of a URL. */ public String toUrlSafe() { try { - return URLEncoder.encode(toString(), UTF_8.name()); + return URLEncoder.encode(toPb().toString(), UTF_8.name()); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Unxpeced encoding exception", e); } } - ByteString toPb() { - return ByteString.copyFrom(bytes); - } - /** * Create a {@code Cursor} given its URL safe encoded form. */ public static Cursor fromUrlSafe(String urlSafe) { try { String utf8Str = URLDecoder.decode(urlSafe, UTF_8.name()); - ByteString byteString = ByteString.copyFromUtf8(utf8Str); - return fromPb(byteString); - } catch (UnsupportedEncodingException e) { + return fromPb(DatastoreV1.Value.parseFrom(utf8Str.getBytes())); + } catch (UnsupportedEncodingException | InvalidProtocolBufferException e) { throw new RuntimeException("Unxpeced decoding exception", e); } } - private static Cursor fromPb(ByteString byteString) { - return new Cursor(byteString.toByteArray()); + public static Cursor copyFrom(byte[] bytes) { + return new Cursor(ByteString.copyFrom(checkNotNull(bytes))); + } + + @Override + protected Value toPb() { + return DatastoreV1.Value.newBuilder().setBlobValue(byteString).build(); + } + + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + return fromPb(DatastoreV1.Value.parseFrom(bytesPb)); + } + + static Cursor fromPb(DatastoreV1.Value valuePb) { + return new Cursor(valuePb.getBlobValue()); } } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java index ac2e048eff0f..ec7052e414e0 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java @@ -27,5 +27,5 @@ public interface DatastoreReader { * * @throws DatastoreServiceException upon failure. */ - QueryResult runQuery(Query query); + QueryResult runQuery(Query query); } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index 250fe0048360..891ed8fd7974 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -1,7 +1,6 @@ package com.google.gcloud.datastore; import com.google.api.services.datastore.DatastoreV1; -import com.google.api.services.datastore.DatastoreV1.BeginTransactionResponse; import com.google.api.services.datastore.client.Datastore; import com.google.api.services.datastore.client.DatastoreException; import com.google.common.collect.AbstractIterator; @@ -49,11 +48,27 @@ public Transaction newTransaction(TransactionOption... transactionOption) { } @Override - public QueryResult runQuery(Query query) { - // TODO To implement - throw new RuntimeException("Not implemented yet"); + public QueryResult runQuery(Query query) { + DatastoreV1.RunQueryRequest.Builder requestPbBuilder = DatastoreV1.RunQueryRequest.newBuilder(); + DatastoreV1.PartitionId.Builder partitionId = DatastoreV1.PartitionId.newBuilder(); + partitionId.setDatasetId(options.dataset()); + String namespace = query.namespace() != null ? query.namespace() : options.namespace(); + if (namespace != null) { + partitionId.setNamespace(namespace); + } + requestPbBuilder.setPartitionId(partitionId.build()); + query.populatePb(requestPbBuilder, 0, null); + DatastoreV1.RunQueryRequest requestPb = requestPbBuilder.build(); + return new QueryResultImpl<>(this, query, requestPb, runQuery(requestPb).getBatch()); } + DatastoreV1.RunQueryResponse runQuery(DatastoreV1.RunQueryRequest requestPb) { + try { + return datastore.runQuery(requestPb); + } catch (DatastoreException e) { + throw DatastoreServiceException.translateAndThrow(e); + } + } @Override public Key allocateId(PartialKey key) { @@ -211,7 +226,8 @@ void comitMutation(DatastoreV1.CommitRequest.Builder requestPb) { ByteString requestTransactionId(DatastoreV1.BeginTransactionRequest.Builder requestPb) { try { - BeginTransactionResponse responsePb = datastore.beginTransaction(requestPb.build()); + DatastoreV1.BeginTransactionResponse responsePb = + datastore.beginTransaction(requestPb.build()); return responsePb.getTransaction(); } catch (DatastoreException e) { throw DatastoreServiceException.translateAndThrow(e); diff --git a/src/main/java/com/google/gcloud/datastore/DateTime.java b/src/main/java/com/google/gcloud/datastore/DateTime.java index 7ade5c46d229..e04d603ff031 100644 --- a/src/main/java/com/google/gcloud/datastore/DateTime.java +++ b/src/main/java/com/google/gcloud/datastore/DateTime.java @@ -2,6 +2,10 @@ import static com.google.common.base.Preconditions.checkNotNull; +import com.google.api.services.datastore.DatastoreV1; +import com.google.api.services.datastore.DatastoreV1.Value; +import com.google.protobuf.InvalidProtocolBufferException; + import org.joda.time.format.ISODateTimeFormat; import java.util.Calendar; @@ -13,11 +17,11 @@ * * @see Google Cloud Datastore Entities, Properties, and Keys */ -public final class DateTime implements java.io.Serializable { +public final class DateTime extends Serializable { private static final long serialVersionUID = 7343324797621228378L; - private final long timestampMicroseconds; + private final transient long timestampMicroseconds; DateTime(long timestampMicroseconds) { this.timestampMicroseconds = timestampMicroseconds; @@ -35,6 +39,9 @@ public int hashCode() { @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } if (!(obj instanceof DateTime)) { return false; } @@ -70,4 +77,14 @@ public static DateTime copyFrom(Date date) { public static DateTime copyFrom(Calendar calendar) { return copyFrom(calendar.getTime()); } + + @Override + protected Value toPb() { + return DatastoreV1.Value.newBuilder().setIntegerValue(timestampMicroseconds).build(); + } + + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + return new DateTime(DatastoreV1.Value.parseFrom(bytesPb).getIntegerValue()); + } } diff --git a/src/main/java/com/google/gcloud/datastore/EntityValue.java b/src/main/java/com/google/gcloud/datastore/EntityValue.java index 3b89d81bb4be..243c1bac7be9 100644 --- a/src/main/java/com/google/gcloud/datastore/EntityValue.java +++ b/src/main/java/com/google/gcloud/datastore/EntityValue.java @@ -42,6 +42,7 @@ private Builder() { @Override public Builder indexed(boolean indexed) { + // see b/8730533 Preconditions.checkArgument(!indexed, "EntityValue can't be indexed"); return super.indexed(indexed); } diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java index 2f8f87ba0f18..ad6e73f867ea 100644 --- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java +++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java @@ -1,26 +1,337 @@ package com.google.gcloud.datastore; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.api.services.datastore.DatastoreV1; +import com.google.common.collect.ImmutableList; +import com.google.common.primitives.Booleans; +import com.google.common.primitives.Doubles; +import com.google.common.primitives.Longs; +import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -public class GqlQuery extends Serializable implements Query { +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; + +/** + * A Google Cloud Datastore GQL. + * + * @see GQL Reference + */ +public final class GqlQuery extends Query { private static final long serialVersionUID = 5988280590929540569L; + private final transient String queryString; + private final transient boolean allowLiteral; + private final transient ImmutableList nameArgs; + private final transient ImmutableList numberArgs; + + private static final class Argument extends Serializable { + + private static final long serialVersionUID = 1976895435257636275L; + + private final transient String name; + private final transient Cursor cursor; + private final transient Value value; + + Argument(String name, Cursor cursor) { + this.name = name; + this.cursor = cursor; + value = null; + } + + Argument(String name, Value value) { + this.name = name; + this.value = value; + cursor = null; + } + + String name() { + return name; + } + + @Override + public int hashCode() { + return Objects.hash(name, cursor, value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Argument)) { + return false; + } + Argument other = (Argument) obj; + return Objects.equals(name, other.name) + && Objects.equals(cursor, other.cursor) + && Objects.equals(value, other.value); + } + + @Override + protected DatastoreV1.GqlQueryArg toPb() { + DatastoreV1.GqlQueryArg.Builder argPb = DatastoreV1.GqlQueryArg.newBuilder(); + if (name != null) { + argPb.setName(name); + } + if (cursor != null) { + argPb.setCursor(cursor.byteString()); + } + if (value != null) { + argPb.setValue(value.toPb()); + } + return argPb.build(); + } + + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + return fromPb(DatastoreV1.GqlQueryArg.parseFrom(bytesPb)); + } + + static Argument fromPb(DatastoreV1.GqlQueryArg argPb) { + String name = argPb.hasName() ? argPb.getName() : null; + if (argPb.hasCursor()) { + return new Argument(name, new Cursor(argPb.getCursor())); + } + return new Argument(name, Value.fromPb(argPb.getValue())); + } + } + + public static final class Builder { + + private String namespace; + private String queryString; + private boolean allowLiteral; + private Map nameArgs = new TreeMap<>(); + private List numberArgs = new LinkedList<>(); + + private Builder(String query) { + queryString = checkNotNull(query); + } + + public Builder query(String query) { + queryString = checkNotNull(query); + return this; + } + + public Builder namespace(String namespace) { + this.namespace = namespace; + return this; + } + + public Builder allowLiteral(boolean allowLiteral) { + this.allowLiteral = allowLiteral; + return this; + } + + public Builder clearArguments() { + nameArgs.clear(); + numberArgs.clear(); + return this; + } + + public Builder setArgument(String name, Cursor cursor) { + nameArgs.put(name, new Argument(name, cursor)); + return this; + } + + public Builder setArgument(String name, String... value) { + nameArgs.put(name, toArgument(name, StringValue.MARSHALLER, Arrays.asList(value))); + return this; + } + + public Builder setArgument(String name, long... value) { + nameArgs.put(name, toArgument(name, LongValue.MARSHALLER, Longs.asList(value))); + return this; + } + + public Builder setArgument(String name, double... value) { + nameArgs.put(name, toArgument(name, DoubleValue.MARSHALLER, Doubles.asList(value))); + return this; + } + + public Builder setArgument(String name, boolean... value) { + nameArgs.put(name, toArgument(name, BooleanValue.MARSHALLER, Booleans.asList(value))); + return this; + } + + public Builder setArgument(String name, DateTime... value) { + nameArgs.put(name, toArgument(name, DateTimeValue.MARSHALLER, Arrays.asList(value))); + return this; + } + + public Builder setArgument(String name, Key... value) { + nameArgs.put(name, toArgument(name, KeyValue.MARSHALLER, Arrays.asList(value))); + return this; + } + + public Builder setArgument(String name, PartialEntity... value) { + nameArgs.put(name, toArgument(name, EntityValue.MARSHALLER, Arrays.asList(value))); + return this; + } + + public Builder setArgument(String name, Blob... value) { + nameArgs.put(name, toArgument(name, BlobValue.MARSHALLER, Arrays.asList(value))); + return this; + } + + public Builder addArgument(Cursor cursor) { + numberArgs.add(new Argument(null, cursor)); + return this; + } + + public Builder addArgument(String... value) { + numberArgs.add(toArgument(StringValue.MARSHALLER, Arrays.asList(value))); + return this; + } + + public Builder addArgument(long... value) { + numberArgs.add(toArgument(LongValue.MARSHALLER, Longs.asList(value))); + return this; + } + + public Builder addArgument(double... value) { + numberArgs.add(toArgument(DoubleValue.MARSHALLER, Doubles.asList(value))); + return this; + } + + public Builder addArgument(boolean... value) { + numberArgs.add(toArgument(BooleanValue.MARSHALLER, Booleans.asList(value))); + return this; + } + + public Builder addArgument(DateTime... value) { + numberArgs.add(toArgument(DateTimeValue.MARSHALLER, Arrays.asList(value))); + return this; + } + + public Builder addArgument(Key... value) { + numberArgs.add(toArgument(KeyValue.MARSHALLER, Arrays.asList(value))); + return this; + } + + public Builder addArgument(PartialEntity... value) { + numberArgs.add(toArgument(EntityValue.MARSHALLER, Arrays.asList(value))); + return this; + } + + public Builder addArgument(Blob... value) { + numberArgs.add(toArgument(BlobValue.MARSHALLER, Arrays.asList(value))); + return this; + } + + @SuppressWarnings("rawtypes") + private static Argument toArgument(Value.BuilderFactory builderFactory, List values) { + return toArgument(null, builderFactory, values); + } + + public GqlQuery build() { + return new GqlQuery(this); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private static Argument toArgument(String name, Value.BuilderFactory builderFactory, + List values) { + List> list = new ArrayList<>(values.size()); + for (Object object : values) { + list.add(builderFactory.newBuilder(object).build()); + } + Value value; + if (list.isEmpty()) { + value = new NullValue(); + } else if (list.size() == 1) { + value = list.get(0); + } else { + value = new ListValue(list); + } + return new Argument(name, value); + } + } + + private GqlQuery(Builder builder) { + super(builder.namespace); + queryString = builder.queryString; + allowLiteral = builder.allowLiteral; + nameArgs = ImmutableList.copyOf(builder.nameArgs.values()); + numberArgs = ImmutableList.copyOf(builder.numberArgs); + } + + @Override + public int hashCode() { + return Objects.hash(namespace(), queryString, allowLiteral, nameArgs, numberArgs); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof GqlQuery)) { + return false; + } + GqlQuery other = (GqlQuery) obj; + return Objects.equals(namespace(), other.namespace()) + && Objects.equals(queryString, other.queryString) + && allowLiteral == other.allowLiteral + && Objects.equals(nameArgs, other.nameArgs) + && Objects.equals(numberArgs, other.numberArgs); + } + @Override protected DatastoreV1.GqlQuery toPb() { - // TODO Auto-generated method stub - return null; + DatastoreV1.GqlQuery.Builder queryPb = DatastoreV1.GqlQuery.newBuilder(); + queryPb.setQueryString(queryString); + queryPb.setAllowLiteral(allowLiteral); + for (Argument argument : nameArgs) { + queryPb.addNameArg(argument.toPb()); + } + for (Argument argument : numberArgs) { + queryPb.addNumberArg(argument.toPb()); + } + return queryPb.build(); } @Override - protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { - // TODO Auto-generated method stub - return null; + protected void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb, int totalRead, + ByteString batchCursor) { + if (batchCursor == null) { + requestPb.setGqlQuery(toPb()); + return; + } + // see b/18705483 + throw new UnsupportedOperationException("paging not implemented yet"); + } + + @Override + protected Object fromPb(String namespace, byte[] bytesPb) throws InvalidProtocolBufferException { + return fromPb(namespace, DatastoreV1.GqlQuery.parseFrom(bytesPb)); + } + + static GqlQuery fromPb(String namespace, DatastoreV1.GqlQuery queryPb) { + Builder builder = new Builder(queryPb.getQueryString()); + builder.namespace(namespace); + if (queryPb.hasAllowLiteral()) { + builder.allowLiteral = queryPb.getAllowLiteral(); + } + for (DatastoreV1.GqlQueryArg nameArg : queryPb.getNameArgList()) { + Argument argument = Argument.fromPb(nameArg); + builder.nameArgs.put(argument.name(), argument); + } + for (DatastoreV1.GqlQueryArg numberArg : queryPb.getNumberArgList()) { + Argument argument = Argument.fromPb(numberArg); + builder.numberArgs.add(argument); + } + return builder.build(); } - static GqlQuery fromPb(DatastoreV1.GqlQuery queryPb) { - // TODO Auto-generated method stub - return null; + public static Builder builder(String query) { + return new Builder(query); } } diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index b8a027069a85..34ce97f8972d 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -148,6 +148,9 @@ public int hashCode() { @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } if (!(obj instanceof Key)) { return false; } diff --git a/src/main/java/com/google/gcloud/datastore/ListValue.java b/src/main/java/com/google/gcloud/datastore/ListValue.java index cd33c96dcff0..b9d7aa66c21c 100644 --- a/src/main/java/com/google/gcloud/datastore/ListValue.java +++ b/src/main/java/com/google/gcloud/datastore/ListValue.java @@ -68,6 +68,7 @@ public Builder addValue(Value first, Value... other) { @Override public Builder indexed(boolean indexed) { + // see b/18704917 DatastoreServiceException.throwInvalidRequest("ListValue can't specify index"); return this; } @@ -78,10 +79,10 @@ public Builder indexed(boolean indexed) { * @see com.google.gcloud.datastore.Value.BaseBuilder#set(java.lang.Object) */ @Override - public Builder set(List> properties) { + public Builder set(List> values) { listBuilder = ImmutableList.>builder(); - for (Value property : properties) { - addValue(property); + for (Value value : values) { + addValue(value); } return this; } @@ -98,8 +99,8 @@ public ListValue build() { } } - public ListValue(List> properties) { - this(builder().set(properties)); + public ListValue(List> values) { + this(builder().set(values)); } public ListValue(Value first, Value... other) { diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java index ccd754bc0712..41cabfccb0ff 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java @@ -65,6 +65,9 @@ public int hashCode() { @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } if (!(obj instanceof PartialEntity)) { return false; } diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index e470a71c3376..74f5d5157bf0 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -52,6 +52,9 @@ public int hashCode() { @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } if (!(obj instanceof PartialKey) || !PartialKey.class.equals(obj.getClass())) { return false; } diff --git a/src/main/java/com/google/gcloud/datastore/PathElement.java b/src/main/java/com/google/gcloud/datastore/PathElement.java index 213c5a7bdae8..34dcc06de4e6 100644 --- a/src/main/java/com/google/gcloud/datastore/PathElement.java +++ b/src/main/java/com/google/gcloud/datastore/PathElement.java @@ -63,6 +63,9 @@ public int hashCode() { @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } if (!(obj instanceof PathElement)) { return false; } diff --git a/src/main/java/com/google/gcloud/datastore/Query.java b/src/main/java/com/google/gcloud/datastore/Query.java index 822b7c6dc472..5479fa4f761e 100644 --- a/src/main/java/com/google/gcloud/datastore/Query.java +++ b/src/main/java/com/google/gcloud/datastore/Query.java @@ -1,7 +1,43 @@ package com.google.gcloud.datastore; -import java.io.Serializable; +import com.google.api.services.datastore.DatastoreV1; +import com.google.common.base.MoreObjects; +import com.google.common.base.MoreObjects.ToStringHelper; +import com.google.protobuf.ByteString; +import com.google.protobuf.GeneratedMessage; +import com.google.protobuf.InvalidProtocolBufferException; -public interface Query extends Serializable { +public abstract class Query extends Serializable { + + private static final long serialVersionUID = -2748141759901313101L; + + private final String namespace; + + Query(String namespace) { + this.namespace = namespace; + } + + public String namespace() { + return namespace; + } + + @Override + public String toString() { + ToStringHelper toStringHelper = MoreObjects.toStringHelper(this); + toStringHelper.add("namespace", namespace); + toStringHelper.add("queryPb", super.toString()); + return toStringHelper.toString(); + } + + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + return fromPb(namespace, bytesPb); + } + + protected abstract Object fromPb(String namespace, byte[] bytesPb) + throws InvalidProtocolBufferException; + + protected abstract void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb, int totalRead, + ByteString batchCursor); } diff --git a/src/main/java/com/google/gcloud/datastore/QueryResult.java b/src/main/java/com/google/gcloud/datastore/QueryResult.java index 8029c2654ce3..47466950205e 100644 --- a/src/main/java/com/google/gcloud/datastore/QueryResult.java +++ b/src/main/java/com/google/gcloud/datastore/QueryResult.java @@ -11,7 +11,7 @@ * * @param V the type of values the result holds. */ -public interface QueryResult extends Iterator { +public interface QueryResult extends Iterator { /** * The result's type. @@ -22,19 +22,22 @@ public interface QueryResult extends Iterator { enum Type { FULL(Entity.class) { - @Override Object convert(DatastoreV1.Entity value) { + @SuppressWarnings("unchecked") + @Override Entity convert(DatastoreV1.Entity value) { return Entity.fromPb(value); } }, PROJECTION(PartialEntity.class) { - @Override Object convert(DatastoreV1.Entity value) { + @SuppressWarnings("unchecked") + @Override PartialEntity convert(DatastoreV1.Entity value) { return PartialEntity.fromPb(value); } }, KEY_ONLY(Key.class) { - @Override Object convert(DatastoreV1.Entity value) { + @SuppressWarnings("unchecked") + @Override Key convert(DatastoreV1.Entity value) { return Key.fromPb(value.getKey()); } }; @@ -49,7 +52,7 @@ public Class getResultClass() { return resultClass; } - abstract Object convert(DatastoreV1.Entity value); + abstract T convert(DatastoreV1.Entity value); } /** diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java index aa237944ca42..d7b48f20ade8 100644 --- a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java +++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java @@ -1,19 +1,23 @@ package com.google.gcloud.datastore; import com.google.api.services.datastore.DatastoreV1; -import com.google.api.services.datastore.DatastoreV1.EntityResult; import com.google.common.collect.ImmutableMap; +import com.google.protobuf.ByteString; import java.util.Iterator; -public class QueryResultImpl implements QueryResult { +class QueryResultImpl implements QueryResult { private static final ImmutableMap RESULT_TYPE_CONVERTER; + private final DatastoreServiceImpl datastore; + private final Query query; private final QueryResult.Type type; - private Iterator entityResultPbIter; - private boolean moreResult; + private DatastoreV1.RunQueryRequest requestPb; + private Iterator entityResultPbIter; + private ByteString endCursor; + private int count; static { ImmutableMap.Builder builder = @@ -24,26 +28,39 @@ public class QueryResultImpl implements QueryResult { RESULT_TYPE_CONVERTER = builder.build(); } - QueryResultImpl(DatastoreV1.Query DatastoreV1.QueryResultBatch resultBatch) { - type = RESULT_TYPE_CONVERTER.get(resultBatch.getEntityResultType()); - entityResultPbIter = resultBatch.getEntityResultList().iterator(); - moreResult = - DatastoreV1.QueryResultBatch.MoreResultsType.NOT_FINISHED == resultBatch.getMoreResults(); + QueryResultImpl(DatastoreServiceImpl datastore, Query query, + DatastoreV1.RunQueryRequest requestPb, DatastoreV1.QueryResultBatch resultPb) { + this.datastore = datastore; + this.query = query; + this.requestPb = requestPb; + type = RESULT_TYPE_CONVERTER.get(resultPb.getEntityResultType()); + } + + void setQueryResultBatch(DatastoreV1.QueryResultBatch resultPb) { + entityResultPbIter = resultPb.getEntityResultList().iterator(); + if (DatastoreV1.QueryResultBatch.MoreResultsType.NOT_FINISHED == resultPb.getMoreResults()) { + endCursor = resultPb.getEndCursor(); + } } @Override public boolean hasNext() { - if (entityResultPbIter.hasNext()) { - return true; - } else if (moreResult) { - // need to fetch more and update results (and more results) - } - return false; + return entityResultPbIter.hasNext() || endCursor != null; } @Override - public V next() { - return (V) type.convert(entityResultPbIter.next()); + public T next() { + if (!hasNext() && endCursor != null) { + DatastoreV1.RunQueryRequest.Builder requestPbBuilder = requestPb.toBuilder(); + query.populatePb(requestPbBuilder, count, endCursor); + DatastoreV1.RunQueryRequest newRequestPb = requestPbBuilder.build(); + DatastoreV1.RunQueryResponse responsePb = datastore.runQuery(newRequestPb); + requestPb = newRequestPb; + setQueryResultBatch(responsePb.getBatch()); + } + DatastoreV1.Entity entity = entityResultPbIter.next().getEntity(); + count++; + return type.convert(entity); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/Transaction.java b/src/main/java/com/google/gcloud/datastore/Transaction.java index 4138b8409d98..c04f81114db4 100644 --- a/src/main/java/com/google/gcloud/datastore/Transaction.java +++ b/src/main/java/com/google/gcloud/datastore/Transaction.java @@ -4,6 +4,13 @@ /** * A Google cloud datastore transaction. + * Any write operation that is applied on a transaction will only be sent + * to the Datastore upon {@link #commit}. A call to {@link #rollback} will invalidate + * the transaction and discard the changes. Any read operation that is done by a transaction + * will be part of it and therefore a {@code commit} is guaranteed to fail if an entity + * was modified outside of the transaction after it was read. Write operation on this + * transaction will not be reflected by read operation (as the changes are only sent to + * the Datastore upon {@code commit}. * * @see Google Cloud Datastore transactions */ @@ -41,7 +48,7 @@ public interface Transaction extends DatastoreReader, DatastoreWriter { * @throws DatastoreServiceException upon failure. */ @Override - QueryResult runQuery(Query query); + QueryResult runQuery(Query query); /** * Commit the transaction. diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java index 0b6e4e81b24c..78bb6f520355 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java @@ -41,7 +41,7 @@ public Iterator get(Key key, Key... others) { } @Override - public QueryResult runQuery(Query query) { + public QueryResult runQuery(Query query) { checkValid(); // TODO To implement throw new RuntimeException("Not implemented yet"); diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 92ad4a672460..1bb592b4eb93 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -1,5 +1,6 @@ package com.google.gcloud.datastore; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; @@ -23,6 +24,15 @@ public class SerializationTest { private static final PartialKey INCOMPLETE_KEY2 = PartialKey.builder(KEY1, "v").addAncestor("p", 1).build(); private static final Key KEY2 = Key.builder(KEY1, "v", 2).build(); + private static final DateTime DATE_TIME1 = DateTime.now(); + private static final Blob BLOB1 = Blob.copyFrom(UTF_8.encode("hello world")); + private static final Cursor CURSOR1 = Cursor.copyFrom(new byte[] {1,2}); + private static final GqlQuery GQL1 = + GqlQuery.builder("select * from kind1 where name = @name and age > @1") + .setArgument("name", "name1") + .addArgument(20) + .namespace("ns1") + .build(); private static final KeyValue KEY_VALUE = new KeyValue(KEY1); private static final NullValue NULL_VALUE = NullValue.builder().indexed(true).build(); private static final StringValue STRING_VALUE = new StringValue("hello"); @@ -31,8 +41,7 @@ public class SerializationTest { private static final BooleanValue BOOLEAN_VALUE = new BooleanValue(true); private static final DateTimeValue DATE_AND_TIME_VALUE = new DateTimeValue(DateTime.now()); - private static final BlobValue BLOB_VALUE = - new BlobValue(Blob.copyFrom(new byte[] {10, 0, -2})); + private static final BlobValue BLOB_VALUE = new BlobValue(BLOB1); private static final RawValue RAW_VALUE = new RawValue( DatastoreV1.Value.newBuilder().setBlobKeyValue("blob-key").setMeaning(18).build()); private static final Entity ENTITY1 = Entity.builder(KEY1).build(); @@ -42,6 +51,7 @@ public class SerializationTest { .setProperty("p1", StringValue.builder("hi1").meaning(10).build()) .setProperty("p2", StringValue.builder("hi2").meaning(11).indexed(false).build()) .setProperty("p3", LongValue.builder(100).indexed(false).meaning(100).build()) + .setBlobProperty("blob", BLOB1) .build(); private static final PartialEntity EMBEDDED_ENTITY1 = ENTITY1; private static final PartialEntity EMBEDDED_ENTITY2 = ENTITY2; @@ -94,8 +104,10 @@ public void testValues() throws Exception { @Test public void testTypes() throws Exception { Object[] types = { KEY1, KEY2, INCOMPLETE_KEY1, INCOMPLETE_KEY2, ENTITY1, ENTITY2, - ENTITY3, EMBEDDED_ENTITY1, EMBEDDED_ENTITY2, EMBEDDED_ENTITY3}; + ENTITY3, EMBEDDED_ENTITY1, EMBEDDED_ENTITY2, EMBEDDED_ENTITY3, DATE_TIME1, + BLOB1, CURSOR1, GQL1}; for (Object obj : types) { + System.out.println("KOKO: " + obj); Object copy = serialiazeAndDeserialize(obj); assertEquals(obj, obj); assertEquals(obj, copy); From 61b8cc3c0cb0f6ce5c46252af401420fcea26f20 Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 10 Dec 2014 16:31:12 -0800 Subject: [PATCH 046/771] work in progress for structured query --- .../gcloud/datastore/StructuredQuery.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/main/java/com/google/gcloud/datastore/StructuredQuery.java diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java new file mode 100644 index 000000000000..dcca9d7e45d2 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java @@ -0,0 +1,65 @@ +package com.google.gcloud.datastore; + +import com.google.api.services.datastore.DatastoreV1; +import com.google.gcloud.datastore.DatastoreServiceException.Code; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +public final class StructuredQuery extends Query { + + private static final long serialVersionUID = 546838955624019594L; + + + static class BaseBuilder { + // TODO: impelement and have sub-classes for keys-only, entity and projection + } + + private StructuredQuery(String namespace) { + super(namespace); + } + + @Override + public int hashCode() { + // implement + return 0; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + // implement + return false; + } + + @Override + protected void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb, int totalRead, + ByteString batchCursor) { + if (batchCursor == null) { + requestPb.setQuery(toPb()); + return; + } + throw new UnsupportedOperationException("paging not implemented yet"); + } + + @Override + protected Object fromPb(String namespace, byte[] bytesPb) throws InvalidProtocolBufferException { + return fromPb(namespace, DatastoreV1.Query.parseFrom(bytesPb)); + } + + @Override + protected DatastoreV1.Query toPb() { + // TODO Auto-generated method stub + return null; + } + + static StructuredQuery fromPb(String namespace, DatastoreV1.Query queryPb) { + return null; + } + + static DatastoreV1.Query nextQuery(DatastoreV1.Query query, ByteString cursor) { + // see b/18705483 + throw new DatastoreServiceException(Code.INTERNAL, "paging for gql results is not implemented"); + } +} From c0f18a3d6da46997b9d8c14dc37fd93937cab3e2 Mon Sep 17 00:00:00 2001 From: aozarov Date: Thu, 11 Dec 2014 14:29:33 -0800 Subject: [PATCH 047/771] some refactoring, update documentation and strcutured query work in progress --- .settings/org.eclipse.jdt.core.prefs | 87 ++++++++++++ .../google/gcloud/datastore/BaseEntity.java | 97 ++++++++------ .../google/gcloud/datastore/BatchWriter.java | 16 +++ .../gcloud/datastore/BatchWriterImpl.java | 25 ++-- .../com/google/gcloud/datastore/Blob.java | 2 +- .../datastore/DatastoreServiceImpl.java | 15 ++- .../com/google/gcloud/datastore/GqlQuery.java | 79 +++++------ .../java/com/google/gcloud/datastore/Key.java | 2 +- .../com/google/gcloud/datastore/Query.java | 88 +++++++++++- .../google/gcloud/datastore/QueryResult.java | 44 +++--- .../gcloud/datastore/QueryResultImpl.java | 3 + .../gcloud/datastore/StructuredQuery.java | 78 +++++++++-- .../google/gcloud/datastore/Transaction.java | 24 ++++ .../gcloud/datastore/TransactionImpl.java | 22 +-- .../com/google/gcloud/datastore/Value.java | 2 - .../google/gcloud/datastore/package-info.java | 22 +-- .../datastore/DatastoreServiceTest.java | 125 ++++++++---------- .../gcloud/datastore/SerializationTest.java | 28 ++-- 18 files changed, 515 insertions(+), 244 deletions(-) diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index fe5e054849a5..62f72418e536 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -2,7 +2,94 @@ <<<<<<<=HEAD >>>>>>>=ef72daa81c73f99e2156f5bfe8127591fc6358e9 eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=error +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=warning +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error +org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=warning +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning org.eclipse.jdt.core.compiler.source=1.7 diff --git a/src/main/java/com/google/gcloud/datastore/BaseEntity.java b/src/main/java/com/google/gcloud/datastore/BaseEntity.java index 41a6674cbcee..010c97dc256e 100644 --- a/src/main/java/com/google/gcloud/datastore/BaseEntity.java +++ b/src/main/java/com/google/gcloud/datastore/BaseEntity.java @@ -11,7 +11,7 @@ import java.util.Set; /** - * A base class for entities. + * A base class for entities to hold the properties. */ abstract class BaseEntity extends Serializable { @@ -36,72 +36,78 @@ protected B self() { return (B) this; } - public B clearProperties() { + /** + * Clears all the properties. + */ + public B clear() { properties.clear(); return self(); } - public B removeProperty(String name) { + /** + * Removes a property with the given {@code name}. + */ + public B remove(String name) { properties.remove(name); return self(); } - public B setProperty(String name, Value value) { + public B set(String name, Value value) { properties.put(name, value); return self(); } - public B setNullProperty(String name) { + public B setNull(String name) { properties.put(name, new NullValue()); return self(); } - public B setStringProperty(String name, String value) { + public B set(String name, String value) { properties.put(name, new StringValue(value)); return self(); } - public B setLongProperty(String name, long value) { + public B set(String name, long value) { properties.put(name, new LongValue(value)); return self(); } - public B setDoubleProperty(String name, double value) { + public B set(String name, double value) { properties.put(name, new DoubleValue(value)); return self(); } - public B setBooleanProperty(String name, boolean value) { + public B set(String name, boolean value) { properties.put(name, new BooleanValue(value)); return self(); } - public B setDateTimeProperty(String name, DateTime value) { + public B set(String name, DateTime value) { properties.put(name, new DateTimeValue(value)); return self(); } - public B setKeyProperty(String name, Key value) { + public B set(String name, Key value) { properties.put(name, new KeyValue(value)); return self(); } - public B setEntityProperty(String name, PartialEntity value) { + public B set(String name, PartialEntity value) { properties.put(name, new EntityValue(value)); return self(); } - public B setListProperty(String name, List> values) { + public B set(String name, List> values) { properties.put(name, new ListValue(values)); return self(); } - public B setListProperty(String name, Value... value) { + public B set(String name, Value... value) { properties.put(name, new ListValue(Arrays.asList(value))); return self(); } - public B setBlobProperty(String name, Blob value) { + public B set(String name, Blob value) { properties.put(name, new BlobValue(value)); return self(); } @@ -118,18 +124,18 @@ protected BaseEntity(ImmutableSortedMap> properties) { } /** - * Returns {@code true} if there is such property with the given {@code name}. + * Returns {@code true} if the entity contains a property with the given {@code name}. */ - public boolean hasProperty(String name) { + public boolean contains(String name) { return properties.containsKey(name); } /** - * Returns the {@link Value} of property with the given {@code name}. + * Returns the {@link Value} for the given property {@code name}. * * @throws DatastoreServiceException if not such property. */ - public > V property(String name) { + public > V getValue(String name) { @SuppressWarnings("unchecked") V property = (V) properties.get(name); if (property == null) { @@ -138,63 +144,66 @@ public > V property(String name) { return property; } - public Type propertyType(String name) { - return property(name).type(); + public Type type(String name) { + return getValue(name).type(); } - public boolean isNullProperty(String name) { - return property(name) instanceof NullValue; + public boolean isNull(String name) { + return getValue(name) instanceof NullValue; } - public String stringProperty(String name) { - return ((StringValue) property(name)).get(); + public String getString(String name) { + return ((StringValue) getValue(name)).get(); } - public long longProperty(String name) { - return ((LongValue) property(name)).get(); + public long getLong(String name) { + return ((LongValue) getValue(name)).get(); } - public double doubleProperty(String name) { - return ((DoubleValue) property(name)).get(); + public double getDouble(String name) { + return ((DoubleValue) getValue(name)).get(); } - public boolean booleanProperty(String name) { - return ((BooleanValue) property(name)).get(); + public boolean getBoolean(String name) { + return ((BooleanValue) getValue(name)).get(); } - public DateTime dateTimeProperty(String name) { - return ((DateTimeValue) property(name)).get(); + public DateTime getDateTime(String name) { + return ((DateTimeValue) getValue(name)).get(); } - public Key keyProperty(String name) { - return ((KeyValue) property(name)).get(); + public Key getKey(String name) { + return ((KeyValue) getValue(name)).get(); } @SuppressWarnings("unchecked") - public T entityProperty(String name) { - return (T) ((EntityValue) property(name)).get(); + public T getEntity(String name) { + return (T) ((EntityValue) getValue(name)).get(); } - public List> listProperty(String name) { - return ((ListValue) property(name)).get(); + public List> getList(String name) { + return ((ListValue) getValue(name)).get(); } - public Blob blobProperty(String name) { - return ((BlobValue) property(name)).get(); + public Blob getBlob(String name) { + return ((BlobValue) getValue(name)).get(); } /** * Returns the property's value as a {@link RawValue}. */ - public RawValue asRawValueProperty(String name) { - Value value = property(name); + public RawValue asRawValue(String name) { + Value value = getValue(name); if (value instanceof RawValue) { return (RawValue) value; } return new RawValue(value.toPb()); } - public Set propertyNames() { + /** + * Returns the properties name. + */ + public Set names() { return properties.keySet(); } diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriter.java b/src/main/java/com/google/gcloud/datastore/BatchWriter.java index 22fe2ded6179..99aeafabd3d4 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchWriter.java +++ b/src/main/java/com/google/gcloud/datastore/BatchWriter.java @@ -4,6 +4,17 @@ * An interface to represent a batch of write operations. * Any write operation that is applied on a batch will only be sent * to the Datastore upon {@link #submit} and with as few RPC calls as possible. + * A usage example: + *
 {@code
+ *   Entity entity1 = datastore.get(key1);
+ *   BatchWriter batchWriter = datastore.newBatchWriter();
+ *   Entity entity2 = Entity.builder(key2).set("name", "John").build();
+ *   entity1 = Entity.builder(entity1).clear().setNull("bla").build();
+ *   Entity entity3 = Entity.builder(key3).set("title", new StringValue("title")).build();
+ *   batchWriter.update(entity1);
+ *   batchWriter.add(entity2, entity3);
+ *   batchWriter.submit();
+ * } 
*/ public interface BatchWriter extends DatastoreWriter { @@ -27,4 +38,9 @@ public interface BatchWriter extends DatastoreWriter { * @throws DatastoreServiceException if there was any failure. */ void submit(); + + /** + * Returns {@code true} if batch is still active (was not submitted). + */ + boolean active(); } diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java index 89fbc60742e2..0564d9b18c30 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java +++ b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java @@ -18,7 +18,7 @@ class BatchWriterImpl implements BatchWriter { private final boolean force; protected final DatastoreServiceImpl datastore; - private boolean wasSubmitted = false; + private boolean active = true; BatchWriterImpl(DatastoreServiceImpl datastore, BatchWriteOption... options) { this.datastore = datastore; @@ -31,9 +31,9 @@ class BatchWriterImpl implements BatchWriter { } } - protected void checkValid() { - if (wasSubmitted) { - throwInvalidRequest(getName() + " was already submitted"); + protected void checkActive() { + if (!active) { + throwInvalidRequest(getName() + " is no longer active"); } } @@ -43,7 +43,7 @@ protected String getName() { @Override public void add(Entity... entities) { - checkValid(); + checkActive(); for (Entity entity : entities) { Key key = entity.key(); if (toAdd.containsKey(key) || toUpdate.containsKey(key) || toPut.containsKey(key)) { @@ -60,7 +60,7 @@ public void add(Entity... entities) { @Override public void update(Entity... entities) { - checkValid(); + checkActive(); for (Entity entity : entities) { Key key = entity.key(); if (toDelete.contains(key)) { @@ -77,7 +77,7 @@ public void update(Entity... entities) { @Override public void put(Entity... entities) { - checkValid(); + checkActive(); for (Entity entity : entities) { Key key = entity.key(); toAdd.remove(key); @@ -89,7 +89,7 @@ public void put(Entity... entities) { @Override public void delete(Key... keys) { - checkValid(); + checkActive(); for (Key key : keys) { toAdd.remove(key); toUpdate.remove(key); @@ -100,7 +100,7 @@ public void delete(Key... keys) { @Override public void submit() { - checkValid(); + checkActive(); DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); for (Entity entity : toAdd.values()) { mutationPb.addInsert(entity.toPb()); @@ -120,7 +120,12 @@ public void submit() { DatastoreV1.CommitRequest.Builder requestPb = newCommitRequest(); requestPb.setMutation(mutationPb); datastore.comitMutation(requestPb); - wasSubmitted = true; + active = false; + } + + @Override + public boolean active() { + return active; } protected DatastoreV1.CommitRequest.Builder newCommitRequest() { diff --git a/src/main/java/com/google/gcloud/datastore/Blob.java b/src/main/java/com/google/gcloud/datastore/Blob.java index a176a10e25c3..f1fabe2f4899 100644 --- a/src/main/java/com/google/gcloud/datastore/Blob.java +++ b/src/main/java/com/google/gcloud/datastore/Blob.java @@ -26,7 +26,7 @@ public final class Blob extends Serializable { private static final long serialVersionUID = 3835421019618247721L; - private static final int MAX_LENGTH = 1_000_000; + public static final int MAX_LENGTH = 1_000_000; private final transient ByteString byteString; diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index 891ed8fd7974..8fe92407260f 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -49,14 +49,21 @@ public Transaction newTransaction(TransactionOption... transactionOption) { @Override public QueryResult runQuery(Query query) { + return runQuery(null, query); + } + + QueryResult runQuery(DatastoreV1.ReadOptions readOptionsPb, Query query) { DatastoreV1.RunQueryRequest.Builder requestPbBuilder = DatastoreV1.RunQueryRequest.newBuilder(); - DatastoreV1.PartitionId.Builder partitionId = DatastoreV1.PartitionId.newBuilder(); - partitionId.setDatasetId(options.dataset()); + if (readOptionsPb != null) { + requestPbBuilder.setReadOptions(readOptionsPb); + } + DatastoreV1.PartitionId.Builder partitionIdPb = DatastoreV1.PartitionId.newBuilder(); + partitionIdPb.setDatasetId(options.dataset()); String namespace = query.namespace() != null ? query.namespace() : options.namespace(); if (namespace != null) { - partitionId.setNamespace(namespace); + partitionIdPb.setNamespace(namespace); } - requestPbBuilder.setPartitionId(partitionId.build()); + requestPbBuilder.setPartitionId(partitionIdPb.build()); query.populatePb(requestPbBuilder, 0, null); DatastoreV1.RunQueryRequest requestPb = requestPbBuilder.build(); return new QueryResultImpl<>(this, query, requestPb, runQuery(requestPb).getBatch()); diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java index ad6e73f867ea..5e0a5c89323f 100644 --- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java +++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java @@ -23,7 +23,7 @@ * * @see GQL Reference */ -public final class GqlQuery extends Query { +final class GqlQuery extends Query { private static final long serialVersionUID = 5988280590929540569L; @@ -104,125 +104,130 @@ static Argument fromPb(DatastoreV1.GqlQueryArg argPb) { } } - public static final class Builder { + /** + * A GQL query builder. + */ + public static final class Builder { + private final ResultType resultType; private String namespace; private String queryString; private boolean allowLiteral; private Map nameArgs = new TreeMap<>(); private List numberArgs = new LinkedList<>(); - private Builder(String query) { + Builder(ResultType resultType, String query) { + this.resultType = resultType; queryString = checkNotNull(query); } - public Builder query(String query) { + public Builder query(String query) { queryString = checkNotNull(query); return this; } - public Builder namespace(String namespace) { + public Builder namespace(String namespace) { this.namespace = namespace; return this; } - public Builder allowLiteral(boolean allowLiteral) { + public Builder allowLiteral(boolean allowLiteral) { this.allowLiteral = allowLiteral; return this; } - public Builder clearArguments() { + public Builder clearArguments() { nameArgs.clear(); numberArgs.clear(); return this; } - public Builder setArgument(String name, Cursor cursor) { + public Builder setArgument(String name, Cursor cursor) { nameArgs.put(name, new Argument(name, cursor)); return this; } - public Builder setArgument(String name, String... value) { + public Builder setArgument(String name, String... value) { nameArgs.put(name, toArgument(name, StringValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder setArgument(String name, long... value) { + public Builder setArgument(String name, long... value) { nameArgs.put(name, toArgument(name, LongValue.MARSHALLER, Longs.asList(value))); return this; } - public Builder setArgument(String name, double... value) { + public Builder setArgument(String name, double... value) { nameArgs.put(name, toArgument(name, DoubleValue.MARSHALLER, Doubles.asList(value))); return this; } - public Builder setArgument(String name, boolean... value) { + public Builder setArgument(String name, boolean... value) { nameArgs.put(name, toArgument(name, BooleanValue.MARSHALLER, Booleans.asList(value))); return this; } - public Builder setArgument(String name, DateTime... value) { + public Builder setArgument(String name, DateTime... value) { nameArgs.put(name, toArgument(name, DateTimeValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder setArgument(String name, Key... value) { + public Builder setArgument(String name, Key... value) { nameArgs.put(name, toArgument(name, KeyValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder setArgument(String name, PartialEntity... value) { + public Builder setArgument(String name, PartialEntity... value) { nameArgs.put(name, toArgument(name, EntityValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder setArgument(String name, Blob... value) { + public Builder setArgument(String name, Blob... value) { nameArgs.put(name, toArgument(name, BlobValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(Cursor cursor) { + public Builder addArgument(Cursor cursor) { numberArgs.add(new Argument(null, cursor)); return this; } - public Builder addArgument(String... value) { + public Builder addArgument(String... value) { numberArgs.add(toArgument(StringValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(long... value) { + public Builder addArgument(long... value) { numberArgs.add(toArgument(LongValue.MARSHALLER, Longs.asList(value))); return this; } - public Builder addArgument(double... value) { + public Builder addArgument(double... value) { numberArgs.add(toArgument(DoubleValue.MARSHALLER, Doubles.asList(value))); return this; } - public Builder addArgument(boolean... value) { + public Builder addArgument(boolean... value) { numberArgs.add(toArgument(BooleanValue.MARSHALLER, Booleans.asList(value))); return this; } - public Builder addArgument(DateTime... value) { + public Builder addArgument(DateTime... value) { numberArgs.add(toArgument(DateTimeValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(Key... value) { + public Builder addArgument(Key... value) { numberArgs.add(toArgument(KeyValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(PartialEntity... value) { + public Builder addArgument(PartialEntity... value) { numberArgs.add(toArgument(EntityValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(Blob... value) { + public Builder addArgument(Blob... value) { numberArgs.add(toArgument(BlobValue.MARSHALLER, Arrays.asList(value))); return this; } @@ -232,8 +237,8 @@ private static Argument toArgument(Value.BuilderFactory builderFactory, List return toArgument(null, builderFactory, values); } - public GqlQuery build() { - return new GqlQuery(this); + public GqlQuery build() { + return new GqlQuery<>(this); } @SuppressWarnings({"unchecked", "rawtypes"}) @@ -255,8 +260,8 @@ private static Argument toArgument(String name, Value.BuilderFactory builderFact } } - private GqlQuery(Builder builder) { - super(builder.namespace); + private GqlQuery(Builder builder) { + super(builder.resultType, builder.namespace); queryString = builder.queryString; allowLiteral = builder.allowLiteral; nameArgs = ImmutableList.copyOf(builder.nameArgs.values()); @@ -276,7 +281,7 @@ public boolean equals(Object obj) { if (!(obj instanceof GqlQuery)) { return false; } - GqlQuery other = (GqlQuery) obj; + GqlQuery other = (GqlQuery) obj; return Objects.equals(namespace(), other.namespace()) && Objects.equals(queryString, other.queryString) && allowLiteral == other.allowLiteral @@ -310,12 +315,14 @@ protected void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb, int tot } @Override - protected Object fromPb(String namespace, byte[] bytesPb) throws InvalidProtocolBufferException { - return fromPb(namespace, DatastoreV1.GqlQuery.parseFrom(bytesPb)); + protected Object fromPb(ResultType resultType, String namespace, byte[] bytesPb) + throws InvalidProtocolBufferException { + return fromPb(resultType, namespace, DatastoreV1.GqlQuery.parseFrom(bytesPb)); } - static GqlQuery fromPb(String namespace, DatastoreV1.GqlQuery queryPb) { - Builder builder = new Builder(queryPb.getQueryString()); + static GqlQuery fromPb(ResultType resultType, String namespace, + DatastoreV1.GqlQuery queryPb) { + Builder builder = new Builder<>(resultType, queryPb.getQueryString()); builder.namespace(namespace); if (queryPb.hasAllowLiteral()) { builder.allowLiteral = queryPb.getAllowLiteral(); @@ -330,8 +337,4 @@ static GqlQuery fromPb(String namespace, DatastoreV1.GqlQuery queryPb) { } return builder.build(); } - - public static Builder builder(String query) { - return new Builder(query); - } } diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index 34ce97f8972d..9687ea9e830e 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -107,7 +107,7 @@ public String name() { } /** - * Returns the key's id (as {@link #Long}) or name (as {@link String}). + * Returns the key's id (as {@link Long}) or name (as {@link String}). */ public Object nameOrId() { return leaf.nameOrId(); diff --git a/src/main/java/com/google/gcloud/datastore/Query.java b/src/main/java/com/google/gcloud/datastore/Query.java index 5479fa4f761e..d0bcaf70de79 100644 --- a/src/main/java/com/google/gcloud/datastore/Query.java +++ b/src/main/java/com/google/gcloud/datastore/Query.java @@ -3,21 +3,78 @@ import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; +import com.google.gcloud.datastore.QueryResult.Type; import com.google.protobuf.ByteString; import com.google.protobuf.GeneratedMessage; import com.google.protobuf.InvalidProtocolBufferException; +// TODO(ozarov): add a usage examples (gql and regular) +/** + * A Google Cloud Datastore query. + * + * @param the type of the values returned by this query. + * @see Datastore Queries + */ public abstract class Query extends Serializable { private static final long serialVersionUID = -2748141759901313101L; + private final ResultType resultType; private final String namespace; - Query(String namespace) { + public static class ResultType implements java.io.Serializable { + + private static final long serialVersionUID = 2104157695425806623L; + private static final ResultType UNKNOWN = new ResultType<>(null, null); + private static final ResultType FULL = new ResultType<>(Entity.class, Type.FULL); + private static final ResultType KEY_ONLY = new ResultType<>(Key.class, Type.KEY_ONLY); + private static final ResultType PROJECTION = + new ResultType<>(PartialEntity.class, Type.PROJECTION); + + + private final Class clazz; + private final Type type; + + private ResultType(Class clazz, Type type) { + this.clazz = clazz; + this.type = type; + } + + public Type getType() { + return type; + } + + public Class getResultClass() { + return clazz; + } + + public static ResultType unknown() { + return UNKNOWN; + } + + public static ResultType full() { + return FULL; + } + + public static ResultType projection() { + return PROJECTION; + } + + public static ResultType keyOnly() { + return KEY_ONLY; + } + } + + Query(ResultType resultType, String namespace) { + this.resultType = resultType; this.namespace = namespace; } + public ResultType resultType() { + return resultType; + } + public String namespace() { return namespace; } @@ -32,12 +89,37 @@ public String toString() { @Override protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { - return fromPb(namespace, bytesPb); + return fromPb(resultType, namespace, bytesPb); } - protected abstract Object fromPb(String namespace, byte[] bytesPb) + protected abstract Object fromPb(ResultType resultType, String namespace, byte[] bytesPb) throws InvalidProtocolBufferException; protected abstract void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb, int totalRead, ByteString batchCursor); + + /** + * Returns a new GQL query builder. + * + * @see GQL Reference + */ + public static GqlQuery.Builder builder(String gql) { + return builder(ResultType.unknown(), gql); + } + + /** + * Returns a new GQL query builder. + * + * @see GQL Reference + */ + public static GqlQuery.Builder builder(ResultType resultType, String gql) { + return new GqlQuery.Builder<>(resultType, gql); + } + + /** + * Returns a new structured query builder. + */ + public static StructuredQuery.FullBuilder builder() { + return new StructuredQuery.FullBuilder(); + } } diff --git a/src/main/java/com/google/gcloud/datastore/QueryResult.java b/src/main/java/com/google/gcloud/datastore/QueryResult.java index 47466950205e..9045764077e4 100644 --- a/src/main/java/com/google/gcloud/datastore/QueryResult.java +++ b/src/main/java/com/google/gcloud/datastore/QueryResult.java @@ -6,57 +6,53 @@ /** * The result of a Google Cloud Datastore query submission. - * Typically the result's type would be casted to its expected type (The {@link #getResultType()} - * method could be used when the type is not known). + * When the result is not typed it is possible to cast it to its appropriate type according to + * the {@link #getType} value. * * @param V the type of values the result holds. */ -public interface QueryResult extends Iterator { +public interface QueryResult extends Iterator { /** - * The result's type. - * FULL: A complete {@link Entity}. - * PROJECTION: A partial entity, represented by {@link PartialEntity}. - * KEY_ONLY: An entity's {@link Key}. + * Possible results types are: + * FULL: A complete {@link Entity}. + * PROJECTION: A partial entity, represented by {@link PartialEntity}. + * KEY_ONLY: An entity's {@link Key}. */ enum Type { - FULL(Entity.class) { + FULL { + @Override @SuppressWarnings("unchecked") - @Override Entity convert(DatastoreV1.Entity value) { + Entity convert(DatastoreV1.Entity value) { return Entity.fromPb(value); } }, - PROJECTION(PartialEntity.class) { + PROJECTION { + + @Override @SuppressWarnings("unchecked") - @Override PartialEntity convert(DatastoreV1.Entity value) { + PartialEntity convert(DatastoreV1.Entity value) { return PartialEntity.fromPb(value); } }, - KEY_ONLY(Key.class) { + KEY_ONLY { + + @Override @SuppressWarnings("unchecked") - @Override Key convert(DatastoreV1.Entity value) { + Key convert(DatastoreV1.Entity value) { return Key.fromPb(value.getKey()); } }; - private final Class resultClass; - - Type(Class resultClass) { - this.resultClass = resultClass; - } - - public Class getResultClass() { - return resultClass; - } - abstract T convert(DatastoreV1.Entity value); } /** - * This method can be used to verify the result type and to cast its value type accordingly. + * Returns the actual type of the result's values. + * When needed the result could be casted accordingly: *
 {@code
    * Type.FULL -> (QueryResult)
    * Type.PROJECTION -> (QueryResult)
diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
index d7b48f20ade8..f24130212ab8 100644
--- a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
@@ -1,5 +1,6 @@
 package com.google.gcloud.datastore;
 
+import com.google.api.client.repackaged.com.google.common.base.Preconditions;
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.common.collect.ImmutableMap;
 import com.google.protobuf.ByteString;
@@ -34,6 +35,8 @@ class QueryResultImpl implements QueryResult {
     this.query = query;
     this.requestPb = requestPb;
     type = RESULT_TYPE_CONVERTER.get(resultPb.getEntityResultType());
+    Preconditions.checkState(query.resultType().getType() == null
+        || query.resultType().getType() == type, "Unexpected result type");
   }
 
   void setQueryResultBatch(DatastoreV1.QueryResultBatch resultPb) {
diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index dcca9d7e45d2..890dca19de4b 100644
--- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -1,21 +1,74 @@
 package com.google.gcloud.datastore;
 
 import com.google.api.services.datastore.DatastoreV1;
-import com.google.gcloud.datastore.DatastoreServiceException.Code;
 import com.google.protobuf.ByteString;
 import com.google.protobuf.InvalidProtocolBufferException;
 
-public final class StructuredQuery extends Query {
+final class StructuredQuery extends Query {
 
   private static final long serialVersionUID = 546838955624019594L;
 
 
-  static class BaseBuilder {
-    // TODO: impelement and have sub-classes for keys-only, entity and projection
+  static class BaseBuilder> {
+
+    private String kind;
+    private String namespace;
+    private Cursor startCursor;
+    private Cursor endCursor;
+    private Integer offset;
+    private Integer limit;
+
+
+    protected B self() {
+      return (B) this;
+    }
+
+    public B kind(String kind) {
+      this.kind = kind;
+      return self();
+    }
+
+    public B namespace(String namespace) {
+      this.namespace = namespace;
+      return self();
+    }
+
+    public B startCursor(Cursor startCursor) {
+      this.startCursor = startCursor;
+      return self();
+    }
+
+    public B encCursor(Cursor endCursor) {
+      this.endCursor = endCursor;
+      return self();
+    }
+
+    public B offset(int offset) {
+      this.offset = offset;
+      return self();
+    }
+
+    public B limit(int limit) {
+      this.limit = limit;
+      return self();
+    }
   }
 
-  private StructuredQuery(String namespace) {
-    super(namespace);
+  public static final class FullBuilder extends BaseBuilder {
+
+  }
+
+  public static final class KeyOnlyBuilder extends BaseBuilder {
+
+  }
+
+  public static final class ProjectionBuilder
+      extends BaseBuilder {
+
+  }
+
+  private StructuredQuery(ResultType resultType, String namespace) {
+    super(resultType, namespace);
   }
 
   @Override
@@ -44,8 +97,9 @@ protected void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb, int tot
   }
 
   @Override
-  protected Object fromPb(String namespace, byte[] bytesPb) throws InvalidProtocolBufferException {
-    return fromPb(namespace, DatastoreV1.Query.parseFrom(bytesPb));
+  protected Object fromPb(ResultType resultType, String namespace, byte[] bytesPb)
+      throws InvalidProtocolBufferException {
+    return fromPb(resultType, namespace, DatastoreV1.Query.parseFrom(bytesPb));
   }
 
   @Override
@@ -54,12 +108,8 @@ protected DatastoreV1.Query toPb() {
     return null;
   }
 
-  static StructuredQuery fromPb(String namespace, DatastoreV1.Query queryPb) {
+  static  StructuredQuery fromPb(ResultType resultType, String namespace,
+      DatastoreV1.Query queryPb) {
     return null;
   }
-
-  static DatastoreV1.Query nextQuery(DatastoreV1.Query query, ByteString cursor) {
-    // see b/18705483
-    throw new DatastoreServiceException(Code.INTERNAL, "paging for gql results is not implemented");
-  }
 }
diff --git a/src/main/java/com/google/gcloud/datastore/Transaction.java b/src/main/java/com/google/gcloud/datastore/Transaction.java
index c04f81114db4..d47a63ddbf1e 100644
--- a/src/main/java/com/google/gcloud/datastore/Transaction.java
+++ b/src/main/java/com/google/gcloud/datastore/Transaction.java
@@ -11,8 +11,27 @@
  * was modified outside of the transaction after it was read. Write operation on this
  * transaction will not be reflected by read operation (as the changes are only sent to
  * the Datastore upon {@code commit}.
+ * A usage example:
+ * 
 {@code
+ *   Transaction transaction = datastore.newTransaction();
+ *   try {
+ *     Entity entity = transaction.get(key);
+ *     if (!entity.contains("last_name") || entity.isNull("last_name")) {
+ *       String[] name = entity.getString("name").split(" ");
+ *       entity = Entity.builder(entity).remove("name").set("first_name", name[0])
+ *           .set("last_name", name[1]).build();
+ *       transaction.update(entity);
+ *       transaction.commit();
+ *     }
+ *   } finally {
+ *     if (transaction.active()) {
+ *       transaction.rollback();
+ *     }
+ *   }
+ * } 
* * @see Google Cloud Datastore transactions + * */ public interface Transaction extends DatastoreReader, DatastoreWriter { @@ -61,4 +80,9 @@ public interface Transaction extends DatastoreReader, DatastoreWriter { * Rollback the transaction. */ void rollback(); + + /** + * Returns {@code true} if the transaction is still active (was not committed or rolledback). + */ + boolean active(); } diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java index 78bb6f520355..ee0dc31685ed 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java @@ -34,7 +34,7 @@ public Entity get(Key key) { @Override public Iterator get(Key key, Key... others) { - checkValid(); + checkActive(); DatastoreV1.ReadOptions.Builder readOptionsPb = DatastoreV1.ReadOptions.newBuilder(); readOptionsPb.setTransaction(transaction); return datastore.get(readOptionsPb.build(), key, others); @@ -42,9 +42,10 @@ public Iterator get(Key key, Key... others) { @Override public QueryResult runQuery(Query query) { - checkValid(); - // TODO To implement - throw new RuntimeException("Not implemented yet"); + checkActive(); + DatastoreV1.ReadOptions.Builder readOptionsPb = DatastoreV1.ReadOptions.newBuilder(); + readOptionsPb.setTransaction(transaction); + return datastore.runQuery(readOptionsPb.build(), query); } @Override @@ -54,23 +55,28 @@ public void commit() { @Override public void rollback() { - super.checkValid(); + super.checkActive(); if (!wasRolledback) { datastore.rollbackTransaction(transaction); } wasRolledback = true; } + @Override + public boolean active() { + return super.active() && !wasRolledback; + } + @Override protected String getName() { return "transaction"; } @Override - protected void checkValid() { - super.checkValid(); + protected void checkActive() { + super.checkActive(); if (wasRolledback) { - throwInvalidRequest(getName() + " was already rolledback"); + throwInvalidRequest(getName() + " is not active (was rolledback)"); } } diff --git a/src/main/java/com/google/gcloud/datastore/Value.java b/src/main/java/com/google/gcloud/datastore/Value.java index 448efe37ed31..9c569ae24de4 100644 --- a/src/main/java/com/google/gcloud/datastore/Value.java +++ b/src/main/java/com/google/gcloud/datastore/Value.java @@ -19,8 +19,6 @@ * Unsupported value (deprecated or unrecognized) would be represented by {@link RawValue}. * * @param the type of the content for this value - * @param

the type of this value - * @param the type of this value's builder */ public abstract class Value extends Serializable { diff --git a/src/main/java/com/google/gcloud/datastore/package-info.java b/src/main/java/com/google/gcloud/datastore/package-info.java index 5b2b8b8e8db5..b186edb83b0e 100644 --- a/src/main/java/com/google/gcloud/datastore/package-info.java +++ b/src/main/java/com/google/gcloud/datastore/package-info.java @@ -1,6 +1,6 @@ /** * A client to the Google Cloud Datastore. - * Typical usage would be: + * A simple usage example: *

 {@code
  * DatastoreServiceOptions options = DatastoreServiceOptions.builder().dataset(DATASET).build();
  * DatastoreService datastore = DatastoreServiceFactory.getDefault(options);
@@ -9,21 +9,21 @@
  * Entity entity = datastore.get(key);
  * if (entity == null) {
  *   entity = Entity.builder(key)
- *       .setStringProperty("name", "John Do")
- *       .setProperty("age", LongValue.builder(100).indexed(false).build())
- *       .setBooleanProperty("updated", false)
+ *       .set("name", "John Do")
+ *       .set("age", LongValue.builder(100).indexed(false).build())
+ *       .set("updated", false)
  *       .build();
  *   datastore.put(entity);
  * } else {
- *   boolean updated = entity.booleanProperty("updated");
+ *   boolean updated = entity.getBoolean("updated");
  *   if (!updated) {
- *     String[] name = entity.stringProperty("name").split(" ");
+ *     String[] name = entity.getString("name").split(" ");
  *     entity = Entity.builder(entity)
- *         .setStringProperty("name", name[0])
- *         .setProperty("last_name", StringValue.builder(name[1]).indexed(false).build())
- *         .setBooleanProperty("updated", true)
- *         .removeProperty("old_property")
- *         .setDoubleProperty("new_property", 1.1)
+ *         .set("name", name[0])
+ *         .set("last_name", StringValue.builder(name[1]).indexed(false).build())
+ *         .set("updated", true)
+ *         .remove("old_property")
+ *         .set("new_property", 1.1)
  *         .build();
  *     datastore.update(entity);
  *   }
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
index 23567330bbc8..0e22bd8ce195 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
@@ -38,33 +38,16 @@ public class DatastoreServiceTest {
       .build();
   private static final ListValue LIST_VALUE2 = new ListValue(Collections.singletonList(KEY_VALUE));
   private static final PartialEntity PARTIAL_ENTITY1 = PartialEntity.builder(PARTIAL_KEY2)
-      .setProperty("str", STR_VALUE)
-      .setProperty("bool", BOOL_VALUE)
-      .setProperty("list", LIST_VALUE1)
-      .build();
+      .set("str", STR_VALUE).set("bool", BOOL_VALUE).set("list", LIST_VALUE1).build();
   private static final PartialEntity PARTIAL_ENTITY2 = PartialEntity.builder(PARTIAL_ENTITY1)
-      .removeProperty("str")
-      .setBooleanProperty("bool", true)
-      .setListProperty("list", LIST_VALUE1.get())
-      .build();
-  private static final Entity ENTITY1 = Entity.builder(KEY1)
-      .setProperty("str", STR_VALUE)
-      .setProperty("bool", BOOL_VALUE)
-      .setProperty("partial1", new EntityValue(PARTIAL_ENTITY1))
-      .setProperty("list", LIST_VALUE2)
-      .build();
-  private static final Entity ENTITY2 = Entity.builder(ENTITY1)
-      .key(KEY2)
-      .removeProperty("str")
-      .setNullProperty("null")
-      .build();
-  private static final Entity ENTITY3 = Entity.builder(ENTITY1)
-      .key(KEY3)
-      .removeProperty("str")
-      .setProperty("null", NULL_VALUE)
-      .setEntityProperty("partial1", PARTIAL_ENTITY2)
-      .setEntityProperty("partial2", ENTITY2)
-      .build();
+      .remove("str").set("bool", true).set("list", LIST_VALUE1.get()).build();
+  private static final Entity ENTITY1 = Entity.builder(KEY1).set("str", STR_VALUE)
+      .set("bool", BOOL_VALUE).set("partial1", new EntityValue(PARTIAL_ENTITY1))
+      .set("list", LIST_VALUE2).build();
+  private static final Entity ENTITY2 = Entity.builder(ENTITY1).key(KEY2).remove("str")
+      .setNull("null").build();
+  private static final Entity ENTITY3 = Entity.builder(ENTITY1).key(KEY3).remove("str")
+      .set("null", NULL_VALUE).set("partial1", PARTIAL_ENTITY2).set("partial2", ENTITY2).build();
 
   private DatastoreServiceOptions options;
   private DatastoreService datastore;
@@ -72,10 +55,12 @@ public class DatastoreServiceTest {
   @Before
   public void setUp() {
     // TODO(ozarov): document that this test depends on a local gcd running.
-    // gcd.sh start dataset1
-    // reference: https://cloud.google.com/datastore/docs/tools/devserver
-    // Or even better, using a "GCE_HOME" param/env initiate and destroy the server
-    // before and after tests via ant or maven
+    // Unfortunately, the gcd tool is not bundled with the cloud SDK and need
+    // to be downloaded independently from
+    // https://cloud.google.com/datastore/docs/tools/devserver (b/16372095).
+    // To start the gcd run:
+    // gcd.sh create dataset1; gcd.sh start dataset1
+    // We should have an option to start the gcd from maven/ant.
     options = DatastoreServiceOptions.builder()
         .dataset(DATASET)
         .host("http://localhost:8080")
@@ -96,8 +81,8 @@ public void testNewTransactionCommit() {
     Transaction transaction = datastore.newTransaction();
     transaction.add(ENTITY3);
     Entity entity2 = Entity.builder(ENTITY2)
-        .clearProperties()
-        .setNullProperty("bla")
+        .clear()
+        .setNull("bla")
         .build();
     transaction.update(entity2);
     transaction.delete(KEY1);
@@ -137,7 +122,7 @@ public void testTransactionWithRead() {
     transaction = datastore.newTransaction();
     assertEquals(ENTITY3, transaction.get(KEY3));
     // update entity3 during the transaction
-    datastore.put(Entity.builder(ENTITY3).clearProperties().build());
+    datastore.put(Entity.builder(ENTITY3).clear().build());
     transaction.update(ENTITY2);
     try {
       transaction.commit();
@@ -148,15 +133,17 @@ public void testTransactionWithRead() {
     }
   }
 
+  @Test
+  public void testTransactionWithQuery() {
+    fail("not implemented");
+  }
+
   @Test
   public void testNewTransactionRollback() {
     Transaction transaction = datastore.newTransaction();
     transaction.add(ENTITY3);
-    Entity entity2 = Entity.builder(ENTITY2)
-        .clearProperties()
-        .setNullProperty("bla")
-        .setListProperty("list3", new StringValue("bla"), StringValue.builder("bla").build())
-        .build();
+    Entity entity2 = Entity.builder(ENTITY2).clear().setNull("bla")
+        .set("list3", new StringValue("bla"), StringValue.builder("bla").build()).build();
     transaction.update(entity2);
     transaction.delete(KEY1);
     transaction.rollback();
@@ -211,17 +198,10 @@ private void verifyNotUsable(DatastoreWriter writer) {
   @Test
   public void testNewBatchWriter() {
     BatchWriter batchWriter = datastore.newBatchWriter();
-    Entity entity1 = Entity.builder(ENTITY1).clearProperties().build();
-    Entity entity2 = Entity.builder(ENTITY2)
-        .clearProperties()
-        .setNullProperty("bla")
-        .build();
-    Entity entity4 = Entity.builder(KEY4)
-        .setProperty("value", new StringValue("value"))
-        .build();
-    Entity entity5 = Entity.builder(KEY5)
-        .setStringProperty("value", "value")
-        .build();
+    Entity entity1 = Entity.builder(ENTITY1).clear().build();
+    Entity entity2 = Entity.builder(ENTITY2).clear().setNull("bla").build();
+    Entity entity4 = Entity.builder(KEY4).set("value", new StringValue("value")).build();
+    Entity entity5 = Entity.builder(KEY5).set("value", "value").build();
     batchWriter.add(entity4, entity5);
     batchWriter.put(ENTITY3, entity1, entity2);
     batchWriter.submit();
@@ -261,7 +241,12 @@ public void testNewBatchWriter() {
   }
 
   @Test
-  public void testRunQuery() {
+  public void testRunGqlQuery() {
+    fail("Not yet implemented");
+  }
+
+  @Test
+  public void testRunStructuredQuery() {
     fail("Not yet implemented");
   }
 
@@ -317,15 +302,15 @@ public void testGet() {
 
     entity = datastore.get(KEY1);
     assertEquals(ENTITY1, entity);
-    StringValue value1 = entity.property("str");
-    BooleanValue value2 = entity.property("bool");
-    ListValue value3 = entity.property("list");
+    StringValue value1 = entity.getValue("str");
+    BooleanValue value2 = entity.getValue("bool");
+    ListValue value3 = entity.getValue("list");
     assertEquals(value1, STR_VALUE);
     assertEquals(value2, BOOL_VALUE);
     assertEquals(value3, LIST_VALUE2);
-    assertEquals(PARTIAL_ENTITY1, entity.entityProperty("partial1"));
-    assertEquals(4, entity.propertyNames().size());
-    assertFalse(entity.hasProperty("bla"));
+    assertEquals(PARTIAL_ENTITY1, entity.getEntity("partial1"));
+    assertEquals(4, entity.names().size());
+    assertFalse(entity.contains("bla"));
   }
 
   @Test
@@ -338,18 +323,18 @@ public void testGetArray() {
     assertEquals(ENTITY2, result.next());
     Entity entity3 = result.next();
     assertEquals(ENTITY3, entity3);
-    assertTrue(entity3.isNullProperty("null"));
-    assertEquals(false, entity3.booleanProperty("bool"));
-    assertEquals(LIST_VALUE2.get(), entity3.listProperty("list"));
-    PartialEntity partial1 = entity3.entityProperty("partial1");
-    Entity partial2 = (Entity) entity3.entityProperty("partial2");
+    assertTrue(entity3.isNull("null"));
+    assertEquals(false, entity3.getBoolean("bool"));
+    assertEquals(LIST_VALUE2.get(), entity3.getList("list"));
+    PartialEntity partial1 = entity3.getEntity("partial1");
+    Entity partial2 = (Entity) entity3.getEntity("partial2");
     assertEquals(partial1, PARTIAL_ENTITY2);
     assertEquals(partial2, ENTITY2);
-    assertEquals(Value.Type.BOOLEAN, entity3.propertyType("bool"));
-    assertEquals(5, entity3.propertyNames().size());
-    assertFalse(entity3.hasProperty("bla"));
+    assertEquals(Value.Type.BOOLEAN, entity3.type("bool"));
+    assertEquals(5, entity3.names().size());
+    assertFalse(entity3.contains("bla"));
     try {
-      entity3.stringProperty("str");
+      entity3.getString("str");
       fail("Expecting a failure");
     } catch (DatastoreServiceException expected) {
       // expected - no such property
@@ -389,10 +374,7 @@ public void testUpdate() {
     }
     datastore.add(ENTITY3);
     assertEquals(ENTITY3, datastore.get(ENTITY3.key()));
-    Entity entity3 = Entity.builder(ENTITY3)
-        .clearProperties()
-        .setProperty("bla", new NullValue())
-        .build();
+    Entity entity3 = Entity.builder(ENTITY3).clear().set("bla", new NullValue()).build();
     assertNotEquals(ENTITY3, entity3);
     datastore.update(entity3);
     assertEquals(entity3, datastore.get(ENTITY3.key()));
@@ -406,10 +388,7 @@ public void testPut() {
     assertNull(keys.next());
     assertFalse(keys.hasNext());
 
-    Entity entity2 = Entity.builder(ENTITY2)
-        .clearProperties()
-        .setProperty("bla", new NullValue())
-        .build();
+    Entity entity2 = Entity.builder(ENTITY2).clear().set("bla", new NullValue()).build();
     assertNotEquals(ENTITY2, entity2);
     datastore.put(ENTITY3, ENTITY1, entity2);
     keys = datastore.get(ENTITY1.key(), ENTITY2.key(), ENTITY3.key());
diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
index 1bb592b4eb93..c95356287ee1 100644
--- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java
+++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
@@ -7,6 +7,7 @@
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.Multimap;
+import com.google.gcloud.datastore.Query.ResultType;
 import com.google.gcloud.datastore.Value.Type;
 
 import org.junit.Test;
@@ -27,8 +28,14 @@ public class SerializationTest {
   private static final DateTime DATE_TIME1 = DateTime.now();
   private static final Blob BLOB1 = Blob.copyFrom(UTF_8.encode("hello world"));
   private static final Cursor CURSOR1 = Cursor.copyFrom(new byte[] {1,2});
-  private static final GqlQuery GQL1 =
-      GqlQuery.builder("select * from kind1 where name = @name and age > @1")
+  private static final Query GQL1 =
+      Query.builder("select * from kind1 where name = @name and age > @1")
+      .setArgument("name", "name1")
+      .addArgument(20)
+      .namespace("ns1")
+      .build();
+  private static final Query GQL2 =
+      Query.builder(ResultType.full(), "select * from kind1 where name = @name and age > @1")
       .setArgument("name", "name1")
       .addArgument(20)
       .namespace("ns1")
@@ -46,18 +53,18 @@ public class SerializationTest {
       DatastoreV1.Value.newBuilder().setBlobKeyValue("blob-key").setMeaning(18).build());
   private static final Entity ENTITY1 = Entity.builder(KEY1).build();
   private static final Entity ENTITY2 =
-      Entity.builder(KEY2).setProperty("null", new NullValue()).build();
+      Entity.builder(KEY2).set("null", new NullValue()).build();
   private static final Entity ENTITY3 = Entity.builder(KEY2)
-      .setProperty("p1", StringValue.builder("hi1").meaning(10).build())
-      .setProperty("p2", StringValue.builder("hi2").meaning(11).indexed(false).build())
-      .setProperty("p3", LongValue.builder(100).indexed(false).meaning(100).build())
-      .setBlobProperty("blob", BLOB1)
+      .set("p1", StringValue.builder("hi1").meaning(10).build())
+      .set("p2", StringValue.builder("hi2").meaning(11).indexed(false).build())
+      .set("p3", LongValue.builder(100).indexed(false).meaning(100).build())
+      .set("blob", BLOB1)
       .build();
   private static final PartialEntity EMBEDDED_ENTITY1 = ENTITY1;
   private static final PartialEntity EMBEDDED_ENTITY2 = ENTITY2;
   private static final PartialEntity EMBEDDED_ENTITY3 = PartialEntity.builder(INCOMPLETE_KEY1)
-      .setProperty("p1", STRING_VALUE)
-      .setProperty("p2", LongValue.builder(100).indexed(false).meaning(100).build())
+      .set("p1", STRING_VALUE)
+      .set("p2", LongValue.builder(100).indexed(false).meaning(100).build())
       .build();
   private static final EntityValue EMBEDDED_ENTITY_VALUE1 =
       new EntityValue(EMBEDDED_ENTITY1);
@@ -105,9 +112,8 @@ public void testValues() throws Exception {
   public void testTypes() throws Exception {
     Object[] types = { KEY1, KEY2, INCOMPLETE_KEY1, INCOMPLETE_KEY2, ENTITY1, ENTITY2,
         ENTITY3, EMBEDDED_ENTITY1, EMBEDDED_ENTITY2, EMBEDDED_ENTITY3, DATE_TIME1,
-        BLOB1, CURSOR1, GQL1};
+        BLOB1, CURSOR1, GQL1, GQL2};
     for (Object obj : types) {
-      System.out.println("KOKO: " + obj);
       Object copy = serialiazeAndDeserialize(obj);
       assertEquals(obj, obj);
       assertEquals(obj, copy);

From ea5765bb5df9d86277c2e452746ead11531329c5 Mon Sep 17 00:00:00 2001
From: aozarov 
Date: Thu, 11 Dec 2014 17:16:46 -0800
Subject: [PATCH 048/771] structured query - work in progress

---
 .../com/google/gcloud/datastore/GqlQuery.java |  45 ++++++-
 .../com/google/gcloud/datastore/Query.java    |   4 +-
 .../gcloud/datastore/StructuredQuery.java     | 125 ++++++++++++------
 .../datastore/DatastoreServiceTest.java       |  19 ++-
 4 files changed, 148 insertions(+), 45 deletions(-)

diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
index 5e0a5c89323f..f8d098d187c5 100644
--- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
@@ -3,7 +3,10 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.api.services.datastore.DatastoreV1;
+import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.primitives.Booleans;
 import com.google.common.primitives.Doubles;
 import com.google.common.primitives.Longs;
@@ -23,7 +26,7 @@
  *
  * @see GQL Reference
  */
-final class GqlQuery extends Query {
+public final class GqlQuery extends Query {
 
   private static final long serialVersionUID = 5988280590929540569L;
 
@@ -32,7 +35,7 @@ final class GqlQuery extends Query {
   private final transient ImmutableList nameArgs;
   private final transient ImmutableList numberArgs;
 
-  private static final class Argument extends Serializable {
+  static final class Argument extends Serializable {
 
     private static final long serialVersionUID = 1976895435257636275L;
 
@@ -42,16 +45,20 @@ private static final class Argument extends Serializable value) {
       this.name = name;
-      this.value = value;
+      this.value = checkNotNull(value);
       cursor = null;
     }
 
+    Object cursorOrValue() {
+      return MoreObjects.firstNonNull(cursor, value);
+    }
+
     String name() {
       return name;
     }
@@ -268,6 +275,36 @@ private GqlQuery(Builder builder) {
     numberArgs = ImmutableList.copyOf(builder.numberArgs);
   }
 
+  public String queryString() {
+    return queryString;
+  }
+
+  public boolean allowLiteral() {
+    return allowLiteral;
+  }
+
+  /**
+   * Returns an immutable map of named arguments.
+   */
+  public Map nameArgs() {
+    ImmutableMap.Builder builder = ImmutableSortedMap.naturalOrder();
+    for (Argument argument : nameArgs) {
+      builder.put(argument.name(), argument.cursorOrValue());
+    }
+    return builder.build();
+  }
+
+  /**
+   * Returns an immutable list of numbered arguments (using original order).
+   */
+  public List numberArgs() {
+    ImmutableList.Builder builder = ImmutableList.builder();
+    for (Argument argument : numberArgs) {
+      builder.add(argument.cursorOrValue());
+    }
+    return builder.build();
+  }
+
   @Override
   public int hashCode() {
     return Objects.hash(namespace(), queryString, allowLiteral, nameArgs, numberArgs);
diff --git a/src/main/java/com/google/gcloud/datastore/Query.java b/src/main/java/com/google/gcloud/datastore/Query.java
index d0bcaf70de79..9ef104ee8d25 100644
--- a/src/main/java/com/google/gcloud/datastore/Query.java
+++ b/src/main/java/com/google/gcloud/datastore/Query.java
@@ -119,7 +119,7 @@ public static  GqlQuery.Builder builder(ResultType resultType, String g
   /**
    * Returns a new structured query builder.
    */
-  public static StructuredQuery.FullBuilder builder() {
-    return new StructuredQuery.FullBuilder();
+  public static StructuredQuery.Builder builder() {
+    return new StructuredQuery.Builder();
   }
 }
diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index 890dca19de4b..ce5c2385899b 100644
--- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -1,80 +1,108 @@
 package com.google.gcloud.datastore;
 
+import com.google.api.client.util.Preconditions;
 import com.google.api.services.datastore.DatastoreV1;
+import com.google.common.collect.ImmutableSet;
 import com.google.protobuf.ByteString;
 import com.google.protobuf.InvalidProtocolBufferException;
 
-final class StructuredQuery extends Query {
+import java.util.Objects;
+
+public final class StructuredQuery extends Query {
 
   private static final long serialVersionUID = 546838955624019594L;
 
+  private final transient boolean keysOnly;
+  private final transient String kind;
+  private final transient ImmutableSet projections;
+  private final transient Cursor startCursor;
+  private final transient Cursor endCursor;
+  private final transient Integer offset;
+  private final transient Integer limit;
 
-  static class BaseBuilder> {
 
-    private String kind;
+  static class Builder {
+
     private String namespace;
+    private String kind;
     private Cursor startCursor;
     private Cursor endCursor;
     private Integer offset;
     private Integer limit;
 
-
-    protected B self() {
-      return (B) this;
+    public Builder namespace(String namespace) {
+      this.namespace = namespace;
+      return this;
     }
 
-    public B kind(String kind) {
+    public Builder kind(String kind) {
       this.kind = kind;
-      return self();
+      return this;
     }
 
-    public B namespace(String namespace) {
-      this.namespace = namespace;
-      return self();
-    }
-
-    public B startCursor(Cursor startCursor) {
+    public Builder startCursor(Cursor startCursor) {
       this.startCursor = startCursor;
-      return self();
+      return this;
     }
 
-    public B encCursor(Cursor endCursor) {
+    public Builder encCursor(Cursor endCursor) {
       this.endCursor = endCursor;
-      return self();
+      return this;
     }
 
-    public B offset(int offset) {
+    public Builder offset(Integer offset) {
+      Preconditions.checkArgument(offset == null || offset >= 0, "offset must not be negative");
       this.offset = offset;
-      return self();
+      return this;
     }
 
-    public B limit(int limit) {
+    public Builder limit(Integer limit) {
+      Preconditions.checkArgument(limit == null || offset > 0, "limit must be positive");
       this.limit = limit;
-      return self();
+      return this;
     }
-  }
-
-  public static final class FullBuilder extends BaseBuilder {
 
-  }
+    public StructuredQuery full() {
+      return new StructuredQuery<>(ResultType.full(), this, false);
+    }
 
-  public static final class KeyOnlyBuilder extends BaseBuilder {
+    public StructuredQuery projection(String projection, String... other) {
+      ImmutableSet projections =
+          ImmutableSet.builder().add(projection).add(other).build();
+      return new StructuredQuery<>(ResultType.projection(), this, projections);
+    }
 
+    public StructuredQuery keyOnly() {
+      return new StructuredQuery<>(ResultType.keyOnly(), this, true);
+    }
   }
 
-  public static final class ProjectionBuilder
-      extends BaseBuilder {
-
+  private StructuredQuery(ResultType resultType, Builder builder,
+      ImmutableSet projections) {
+    super(resultType, builder.namespace);
+    kind = builder.kind;
+    startCursor = builder.startCursor;
+    endCursor = builder.endCursor;
+    offset = builder.offset;
+    limit = builder.limit;
+    this.projections = projections;
+    keysOnly = false;
   }
 
-  private StructuredQuery(ResultType resultType, String namespace) {
-    super(resultType, namespace);
+  private StructuredQuery(ResultType resultType, Builder builder, boolean keysOnly) {
+    super(resultType, builder.namespace);
+    kind = builder.kind;
+    startCursor = builder.startCursor;
+    endCursor = builder.endCursor;
+    offset = builder.offset;
+    limit = builder.limit;
+    this.keysOnly = keysOnly;
+    projections = ImmutableSet.of();
   }
 
   @Override
   public int hashCode() {
-    // implement
-    return 0;
+    return Objects.hash(namespace(), kind, startCursor, endCursor, offset, limit);
   }
 
   @Override
@@ -82,8 +110,16 @@ public boolean equals(Object obj) {
     if (obj == this) {
       return true;
     }
-    // implement
-    return false;
+    if (!(obj instanceof StructuredQuery)) {
+      return false;
+    }
+    StructuredQuery other = (StructuredQuery) obj;
+    return Objects.equals(namespace(), other.namespace())
+        && Objects.equals(kind, other.kind)
+        && Objects.equals(startCursor, other.startCursor)
+        && Objects.equals(endCursor, other.endCursor)
+        && Objects.equals(offset, other.offset)
+        && Objects.equals(limit, other.limit);
   }
 
   @Override
@@ -104,8 +140,23 @@ protected Object fromPb(ResultType resultType, String namespace, byte[] bytes
 
   @Override
   protected DatastoreV1.Query toPb() {
-    // TODO Auto-generated method stub
-    return null;
+    DatastoreV1.Query.Builder queryPb = DatastoreV1.Query.newBuilder();
+    if (kind != null) {
+      queryPb.addKindBuilder().setName(kind);
+    }
+    if (startCursor != null) {
+      queryPb.setStartCursor(startCursor.byteString());
+    }
+    if (endCursor != null) {
+      queryPb.setEndCursor(endCursor.byteString());
+    }
+    if (offset != null) {
+      queryPb.setOffset(offset);
+    }
+    if (limit != null) {
+      queryPb.setLimit(limit);
+    }
+    return queryPb.build();
   }
 
   static  StructuredQuery fromPb(ResultType resultType, String namespace,
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
index 0e22bd8ce195..a9fe6e53c005 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
@@ -241,12 +241,27 @@ public void testNewBatchWriter() {
   }
 
   @Test
-  public void testRunGqlQuery() {
+  public void testRunGqlQueryNoCasting() {
     fail("Not yet implemented");
   }
 
   @Test
-  public void testRunStructuredQuery() {
+  public void testRunGqlQueryWithCasting() {
+    fail("Not yet implemented");
+  }
+
+  @Test
+  public void testRunStructuredQueryFull() {
+    fail("Not yet implemented");
+  }
+
+  @Test
+  public void testRunStructuredQueryProjection() {
+    fail("Not yet implemented");
+  }
+
+  @Test
+  public void testRunStructuredQueryKeysOnly() {
     fail("Not yet implemented");
   }
 

From 393682e16733b65bbc7809199711dfb255ae4c93 Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Thu, 11 Dec 2014 21:10:00 -0800
Subject: [PATCH 049/771] Fix compilation error visible by maven

---
 .classpath                                    | 10 ----
 .settings/org.eclipse.jdt.core.prefs          |  3 --
 pom.xml                                       |  2 +-
 .../com/google/gcloud/datastore/Value.java    | 47 ++++++++-----------
 4 files changed, 20 insertions(+), 42 deletions(-)

diff --git a/.classpath b/.classpath
index d6cf6121af66..9ed6ee4d0713 100644
--- a/.classpath
+++ b/.classpath
@@ -23,15 +23,5 @@
 		
 	
 	
-	
-		
-			
-		
-	
-	
-		
-			
-		
-	
 	
 
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index 62f72418e536..0192f1f052aa 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -8,8 +8,6 @@ org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonN
 org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
 org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
 org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
-org.eclipse.jdt.core.compiler.compliance=1.7
 org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
 org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
 org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
@@ -92,4 +90,3 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
 org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning
 org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
 org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
-org.eclipse.jdt.core.compiler.source=1.7
diff --git a/pom.xml b/pom.xml
index d3431c1584fd..3a573d91459f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -53,7 +53,7 @@
       
         org.apache.maven.plugins
         maven-compiler-plugin
-        2.5.1
+        3.1
         
                 1.7
                 1.7
diff --git a/src/main/java/com/google/gcloud/datastore/Value.java b/src/main/java/com/google/gcloud/datastore/Value.java
index 9c569ae24de4..4686624adf54 100644
--- a/src/main/java/com/google/gcloud/datastore/Value.java
+++ b/src/main/java/com/google/gcloud/datastore/Value.java
@@ -40,85 +40,72 @@ public enum Type {
     /**
      * Represents a {@code null} value.
      */
-    NULL(NullValue.MARSHALLER, NullValue.MARSHALLER),
+    NULL(NullValue.MARSHALLER),
 
     /**
      * Represents a {@code string} value.
      */
-    STRING(StringValue.MARSHALLER, StringValue.MARSHALLER),
+    STRING(StringValue.MARSHALLER),
 
     /**
      * Represents an entity ({@link PartialEntity} or {@link Entity}) value.
      */
-    ENTITY(EntityValue.MARSHALLER, EntityValue.MARSHALLER),
+    ENTITY(EntityValue.MARSHALLER),
 
     /**
      * Represents a {@code list} of {@link Value}s.
      */
-    LIST(ListValue.MARSHALLER, ListValue.MARSHALLER),
+    LIST(ListValue.MARSHALLER),
 
     /**
      * Represents a {@code key} as a value.
      */
-    KEY(KeyValue.MARSHALLER, KeyValue.MARSHALLER),
+    KEY(KeyValue.MARSHALLER),
 
     /**
      * Represents a {@code long} value.
      */
-    LONG(LongValue.MARSHALLER, LongValue.MARSHALLER),
+    LONG(LongValue.MARSHALLER),
 
     /**
      * Represents a {@code double} value.
      */
-    DOUBLE(DoubleValue.MARSHALLER, DoubleValue.MARSHALLER),
+    DOUBLE(DoubleValue.MARSHALLER),
 
     /**
      * Represents a {@code boolean} value.
      */
-    BOOLEAN(BooleanValue.MARSHALLER, BooleanValue.MARSHALLER),
+    BOOLEAN(BooleanValue.MARSHALLER),
 
     /**
      * Represents a {@link DateTime} value.
      */
-    DATE_TIME(DateTimeValue.MARSHALLER, DateTimeValue.MARSHALLER),
+    DATE_TIME(DateTimeValue.MARSHALLER),
 
     /**
      * Represents a {@link Blob} value.
      */
-    BLOB(BlobValue.MARSHALLER, BlobValue.MARSHALLER),
+    BLOB(BlobValue.MARSHALLER),
 
     /**
      * Represents a raw/unparsed value.
      */
-    RAW_VALUE(RawValue.MARSHALLER, RawValue.MARSHALLER);
+    RAW_VALUE(RawValue.MARSHALLER);
 
 
-    @SuppressWarnings("rawtypes") private final BuilderFactory builderFactory;
     @SuppressWarnings("rawtypes") private final Marshaller marshaller;
 
-    , B extends Builder> Type(Marshaller marshaller,
-        BuilderFactory builderFactory) {
+    , B extends Builder> Type(Marshaller marshaller) {
       this.marshaller = marshaller;
-      this.builderFactory = builderFactory;
       int fieldId = marshaller.getProtoFieldId();
       if (fieldId > 0) {
         DESCRIPTOR_TO_TYPE_MAP.put(fieldId, this);
       }
     }
 
-    , B extends Builder> Marshaller getMarshaller() {
+    Marshaller getMarshaller() {
       return marshaller;
     }
-
-    , B extends Builder> BuilderFactory
-        getBuilderFactory() {
-      return builderFactory;
-    }
-  }
-
-  interface BuilderFactory, B extends Builder> {
-
-    B newBuilder(V value);
   }
 
   interface Builder, B extends Builder> {
@@ -142,6 +129,10 @@ interface Builder, B extends Builder> {
     P build();
   }
 
+  interface BuilderFactory, B extends Builder> {
+    B newBuilder(V value);
+  }
+
   interface Marshaller, B extends Builder> {
 
     B fromProto(DatastoreV1.Value proto);
@@ -314,9 +305,9 @@ public boolean equals(Object obj) {
   }
 
   @Override
-  @SuppressWarnings({"unchecked", "rawtypes"})
+  @SuppressWarnings("unchecked")
   protected DatastoreV1.Value toPb() {
-    return type().getMarshaller().toProto((Value) this);
+    return type().getMarshaller().toProto(this);
   }
 
   static Value fromPb(DatastoreV1.Value proto) {

From 81d22393f43ece2d3cf14f83ad14403ae6a6983a Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Thu, 11 Dec 2014 23:21:27 -0800
Subject: [PATCH 050/771] structured query - work in progress

---
 .../com/google/gcloud/datastore/BaseKey.java  |  26 ++-
 .../com/google/gcloud/datastore/GqlQuery.java |  18 ++
 .../java/com/google/gcloud/datastore/Key.java |  26 +--
 .../google/gcloud/datastore/PartialKey.java   |  26 +--
 .../com/google/gcloud/datastore/Query.java    |  18 --
 .../gcloud/datastore/StructuredQuery.java     | 208 ++++++++++++++++--
 .../gcloud/datastore/SerializationTest.java   |   4 +-
 7 files changed, 234 insertions(+), 92 deletions(-)

diff --git a/src/main/java/com/google/gcloud/datastore/BaseKey.java b/src/main/java/com/google/gcloud/datastore/BaseKey.java
index 09edf60afeac..4b4a33aa404e 100644
--- a/src/main/java/com/google/gcloud/datastore/BaseKey.java
+++ b/src/main/java/com/google/gcloud/datastore/BaseKey.java
@@ -11,6 +11,7 @@
 
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Base class for keys.
@@ -147,6 +148,26 @@ public String kind() {
     return kind;
   }
 
+  @Override
+  public int hashCode() {
+    return Objects.hash(dataset(), namespace(), ancestors(), leaf());
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    }
+    if (!(obj instanceof BaseKey)) {
+      return false;
+    }
+    PartialKey other = (PartialKey) obj;
+    return Objects.equals(dataset(), other.dataset())
+        && Objects.equals(namespace(), other.namespace())
+        && Objects.equals(ancestors(), other.ancestors())
+        && Objects.equals(leaf(), other.leaf());
+  }
+
   @Override
   protected DatastoreV1.Key toPb() {
     DatastoreV1.Key.Builder keyPb = DatastoreV1.Key.newBuilder();
@@ -163,9 +184,10 @@ protected DatastoreV1.Key toPb() {
     for (PathElement pathEntry : ancestors) {
       keyPb.addPathElement(pathEntry.toPb());
     }
-    addLeaf(keyPb);
+    PathElement leaf = leaf();
+    keyPb.addPathElement(leaf.toPb());
     return keyPb.build();
   }
 
-  protected abstract void addLeaf(DatastoreV1.Key.Builder keyPb);
+  protected abstract PathElement leaf();
 }
diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
index f8d098d187c5..d7c481d613ee 100644
--- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
@@ -374,4 +374,22 @@ static  GqlQuery fromPb(ResultType resultType, String namespace,
     }
     return builder.build();
   }
+
+  /**
+   * Returns a new GQL query builder.
+   *
+   * @see GQL Reference
+   */
+  public static GqlQuery.Builder builder(String gql) {
+    return builder(ResultType.unknown(), gql);
+  }
+
+  /**
+   * Returns a new GQL query builder.
+   *
+   * @see GQL Reference
+   */
+  public static  GqlQuery.Builder builder(ResultType resultType, String gql) {
+    return new GqlQuery.Builder<>(resultType, gql);
+  }
 }
diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java
index 9687ea9e830e..272fc6e4d7f4 100644
--- a/src/main/java/com/google/gcloud/datastore/Key.java
+++ b/src/main/java/com/google/gcloud/datastore/Key.java
@@ -11,7 +11,6 @@
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
-import java.util.Objects;
 
 /**
  * A key that is guaranteed to be complete and could be used to reference a
@@ -142,29 +141,8 @@ public static Key fromUrlSafe(String urlSafe) {
   }
 
   @Override
-  public int hashCode() {
-    return Objects.hash(dataset(), namespace(), ancestors(), kind(), leaf);
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    if (obj == this) {
-      return true;
-    }
-    if (!(obj instanceof Key)) {
-      return false;
-    }
-    Key other = (Key) obj;
-    return Objects.equals(dataset(), other.dataset())
-        && Objects.equals(namespace(), other.namespace())
-        && Objects.equals(ancestors(), other.ancestors())
-        && Objects.equals(kind(), other.kind())
-        && Objects.equals(leaf, other.leaf);
-  }
-
-  @Override
-  protected void addLeaf(DatastoreV1.Key.Builder keyPb) {
-    keyPb.addPathElement(leaf.toPb());
+  protected PathElement leaf() {
+    return leaf;
   }
 
   @Override
diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java
index 74f5d5157bf0..f7e6c00374c8 100644
--- a/src/main/java/com/google/gcloud/datastore/PartialKey.java
+++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java
@@ -5,7 +5,6 @@
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import java.util.List;
-import java.util.Objects;
 
 /**
  * A partial key (without a name or id).
@@ -46,29 +45,8 @@ public Key newKey(long id) {
   }
 
   @Override
-  public int hashCode() {
-    return Objects.hash(dataset(), namespace(), ancestors(), kind());
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    if (obj == this) {
-      return true;
-    }
-    if (!(obj instanceof PartialKey) || !PartialKey.class.equals(obj.getClass())) {
-      return false;
-    }
-
-    PartialKey other = (PartialKey) obj;
-    return Objects.equals(dataset(), other.dataset())
-        && Objects.equals(namespace(), other.namespace())
-        && Objects.equals(ancestors(), other.ancestors())
-        && Objects.equals(kind(), other.kind());
-  }
-
-  @Override
-  protected void addLeaf(DatastoreV1.Key.Builder keyPb) {
-    keyPb.addPathElement(new PathElement(kind()).toPb());
+  protected PathElement leaf() {
+    return new PathElement(kind());
   }
 
   @Override
diff --git a/src/main/java/com/google/gcloud/datastore/Query.java b/src/main/java/com/google/gcloud/datastore/Query.java
index 9ef104ee8d25..d1c29d189599 100644
--- a/src/main/java/com/google/gcloud/datastore/Query.java
+++ b/src/main/java/com/google/gcloud/datastore/Query.java
@@ -98,24 +98,6 @@ protected abstract Object fromPb(ResultType resultType, String namespace, byt
   protected abstract void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb, int totalRead,
       ByteString batchCursor);
 
-  /**
-   * Returns a new GQL query builder.
-   *
-   * @see GQL Reference
-   */
-  public static GqlQuery.Builder builder(String gql) {
-    return builder(ResultType.unknown(), gql);
-  }
-
-  /**
-   * Returns a new GQL query builder.
-   *
-   * @see GQL Reference
-   */
-  public static  GqlQuery.Builder builder(ResultType resultType, String gql) {
-    return new GqlQuery.Builder<>(resultType, gql);
-  }
-
   /**
    * Returns a new structured query builder.
    */
diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index ce5c2385899b..b5e6a98fe1dc 100644
--- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -1,12 +1,23 @@
 package com.google.gcloud.datastore;
 
+import static com.google.api.client.util.Preconditions.checkNotNull;
+
 import com.google.api.client.util.Preconditions;
 import com.google.api.services.datastore.DatastoreV1;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.protobuf.ByteString;
 import com.google.protobuf.InvalidProtocolBufferException;
 
+import java.io.Serializable;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.Objects;
+import java.util.Set;
 
 public final class StructuredQuery extends Query {
 
@@ -14,13 +25,160 @@ public final class StructuredQuery extends Query {
 
   private final transient boolean keysOnly;
   private final transient String kind;
-  private final transient ImmutableSet projections;
+  private final transient ImmutableList projection;
+  private final transient Filter filter;
+  private final transient ImmutableList groupBy;
+  private final transient ImmutableList orderBy;
   private final transient Cursor startCursor;
   private final transient Cursor endCursor;
   private final transient Integer offset;
   private final transient Integer limit;
 
 
+  public static final class Filter implements Serializable {
+
+    private static final long serialVersionUID = -4514695915258598597L;
+
+    private final String property;
+    private final Operator operator;
+    private final Value value;
+    private final Filter next;
+
+    public enum Operator {
+      LESS_THAN,
+      LESS_THAN_OR_EQUAL,
+      GREATER_THAN,
+      GREATER_THAN_OR_EQUAL,
+      EQUAL,
+      HAS_ANCESTOR,
+      AND
+    }
+
+    private Filter(String property, Operator operator, Value value, Filter next) {
+      this.property = checkNotNull(property);
+      this.operator = checkNotNull(operator);
+      this.value = value;
+      this.next = next;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(property, operator, value, next);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == this) {
+        return true;
+      }
+      if (!(obj instanceof Filter)) {
+        return false;
+      }
+      Filter other = (Filter) obj;
+      return property.equals(other.property)
+          && operator.equals(other.operator)
+          && Objects.equals(value, other.value)
+          && Objects.equals(next, other.next);
+    }
+
+    public static Filter le(String property, Value value) {
+      return new Filter(property, Operator.LESS_THAN, value, null);
+    }
+
+    public static Filter lte(String property, Value value) {
+      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, value, null);
+    }
+
+    public static Filter gt(String property, Value value) {
+      return new Filter(property, Operator.GREATER_THAN, value, null);
+    }
+
+    public static Filter gte(String property, Value value) {
+      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, value, null);
+    }
+
+    public static Filter eq(String property, Value value) {
+      return new Filter(property, Operator.EQUAL, value, null);
+    }
+
+    public static Filter hasAncestor(String property) {
+      return new Filter(property, Operator.HAS_ANCESTOR, null, null);
+    }
+
+    public static Filter and(Filter first, Filter... other) {
+      Set combined = new LinkedHashSet<>();
+      for (Filter f : Iterables.concat(Collections.singleton(first), Arrays.asList(other))) {
+        while (f != null) {
+          combined.add(new Filter(f.property, f.operator, f.value, null));
+          f = f.next;
+        }
+      }
+      ArrayDeque stack = new ArrayDeque<>(combined.size());
+      for (Filter f : combined) {
+        stack.push(f);
+      }
+      while (true) {
+        Filter f1 = stack.pop();
+        if (stack.isEmpty()) {
+          return f1;
+        }
+        Filter f2 = stack.pop();
+        stack.push(new Filter(f2.property, f2.operator, f2.value, f1));
+      }
+    }
+  }
+
+  public static final class OrderBy implements Serializable {
+
+    private static final long serialVersionUID = 4091186784814400031L;
+
+    private final String property;
+    private final Direction direction;
+
+    public enum Direction {
+      ASC, DESC
+    }
+
+    public OrderBy(String property, Direction direction) {
+      this.property = checkNotNull(property);
+      this.direction = checkNotNull(direction);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(property, direction);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == this) {
+        return true;
+      }
+      if (!(obj instanceof OrderBy)) {
+        return false;
+      }
+      OrderBy other = (OrderBy) obj;
+      return property.equals(other.property)
+          && direction.equals(other.direction);
+    }
+
+    public String property() {
+      return property;
+    }
+
+    public Direction direction() {
+      return direction;
+    }
+
+    public static OrderBy asc(String property) {
+      return new OrderBy(property, OrderBy.Direction.ASC);
+    }
+
+    public static OrderBy desc(String property) {
+      return new OrderBy(property, OrderBy.Direction.DESC);
+    }
+  }
+
   static class Builder {
 
     private String namespace;
@@ -62,6 +220,7 @@ public Builder limit(Integer limit) {
       return this;
     }
 
+    /*
     public StructuredQuery full() {
       return new StructuredQuery<>(ResultType.full(), this, false);
     }
@@ -75,34 +234,33 @@ public StructuredQuery projection(String projection, String... ot
     public StructuredQuery keyOnly() {
       return new StructuredQuery<>(ResultType.keyOnly(), this, true);
     }
+    */
   }
 
-  private StructuredQuery(ResultType resultType, Builder builder,
-      ImmutableSet projections) {
-    super(resultType, builder.namespace);
-    kind = builder.kind;
-    startCursor = builder.startCursor;
-    endCursor = builder.endCursor;
-    offset = builder.offset;
-    limit = builder.limit;
-    this.projections = projections;
-    keysOnly = false;
-  }
 
-  private StructuredQuery(ResultType resultType, Builder builder, boolean keysOnly) {
-    super(resultType, builder.namespace);
-    kind = builder.kind;
-    startCursor = builder.startCursor;
-    endCursor = builder.endCursor;
-    offset = builder.offset;
-    limit = builder.limit;
+
+  private StructuredQuery(ResultType resultType, boolean keysOnly, String namespace, String kind,
+      Cursor startCursor, Cursor endCursor, Integer offset, Integer limit,
+      ImmutableList projection, Filter filter, ImmutableList groupBy,
+      ImmutableList orderBy) {
+    super(resultType, namespace);
     this.keysOnly = keysOnly;
-    projections = ImmutableSet.of();
+    this.kind = kind;
+    this.startCursor = startCursor;
+    this.endCursor = endCursor;
+    this.offset = offset;
+    this.limit = limit;
+    this.projection = projection;
+    this.filter = filter;
+    this.groupBy = groupBy;
+    this.orderBy = orderBy;
+    Preconditions.checkState(keysOnly == false || projection.isEmpty() && groupBy.isEmpty(),
+        "projection or group by are not applicable for keys-only queries");
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(namespace(), kind, startCursor, endCursor, offset, limit);
+    return Objects.hash(namespace(), keysOnly, kind, startCursor, endCursor, offset, limit, projection, filter, groupBy, orderBy);
   }
 
   @Override
@@ -119,7 +277,11 @@ public boolean equals(Object obj) {
         && Objects.equals(startCursor, other.startCursor)
         && Objects.equals(endCursor, other.endCursor)
         && Objects.equals(offset, other.offset)
-        && Objects.equals(limit, other.limit);
+        && Objects.equals(limit, other.limit)
+        && Objects.equals(projection, other.projection)
+        && Objects.equals(filter, other.filter)
+        && Objects.equals(groupBy, other.groupBy)
+        && Objects.equals(orderBy, other.orderBy);
   }
 
   @Override
@@ -156,11 +318,13 @@ protected DatastoreV1.Query toPb() {
     if (limit != null) {
       queryPb.setLimit(limit);
     }
+    // TODO: projection, filter, groupBy, orderBy (or keys-only)
     return queryPb.build();
   }
 
   static  StructuredQuery fromPb(ResultType resultType, String namespace,
       DatastoreV1.Query queryPb) {
+    // TODO: implement
     return null;
   }
 }
diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
index c95356287ee1..eb63574ec021 100644
--- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java
+++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
@@ -29,13 +29,13 @@ public class SerializationTest {
   private static final Blob BLOB1 = Blob.copyFrom(UTF_8.encode("hello world"));
   private static final Cursor CURSOR1 = Cursor.copyFrom(new byte[] {1,2});
   private static final Query GQL1 =
-      Query.builder("select * from kind1 where name = @name and age > @1")
+      GqlQuery.builder("select * from kind1 where name = @name and age > @1")
       .setArgument("name", "name1")
       .addArgument(20)
       .namespace("ns1")
       .build();
   private static final Query GQL2 =
-      Query.builder(ResultType.full(), "select * from kind1 where name = @name and age > @1")
+      GqlQuery.builder(ResultType.full(), "select * from kind1 where name = @name and age > @1")
       .setArgument("name", "name1")
       .addArgument(20)
       .namespace("ns1")

From 41422a9d39c4e8c49e1cfa98d32ee2e36ba38c95 Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Fri, 12 Dec 2014 09:21:51 -0800
Subject: [PATCH 051/771] Adding static of method for Values

---
 .../google/gcloud/datastore/BaseEntity.java   | 33 ++++++++++-----
 .../google/gcloud/datastore/BlobValue.java    |  4 ++
 .../google/gcloud/datastore/BooleanValue.java |  4 ++
 .../gcloud/datastore/DateTimeValue.java       |  4 ++
 .../google/gcloud/datastore/DoubleValue.java  |  4 ++
 .../google/gcloud/datastore/EntityValue.java  |  4 ++
 .../com/google/gcloud/datastore/KeyValue.java |  4 ++
 .../google/gcloud/datastore/ListValue.java    |  8 ++++
 .../google/gcloud/datastore/LongValue.java    |  4 ++
 .../google/gcloud/datastore/NullValue.java    |  4 ++
 .../com/google/gcloud/datastore/RawValue.java |  4 ++
 .../google/gcloud/datastore/StringValue.java  |  4 ++
 .../gcloud/datastore/StructuredQuery.java     | 40 +++++++++++++++++++
 .../datastore/DatastoreServiceTest.java       | 14 +++----
 .../gcloud/datastore/SerializationTest.java   | 28 ++++++-------
 15 files changed, 129 insertions(+), 34 deletions(-)

diff --git a/src/main/java/com/google/gcloud/datastore/BaseEntity.java b/src/main/java/com/google/gcloud/datastore/BaseEntity.java
index 010c97dc256e..606630a5b10c 100644
--- a/src/main/java/com/google/gcloud/datastore/BaseEntity.java
+++ b/src/main/java/com/google/gcloud/datastore/BaseEntity.java
@@ -1,5 +1,16 @@
 package com.google.gcloud.datastore;
 
+import static com.google.gcloud.datastore.BlobValue.of;
+import static com.google.gcloud.datastore.BooleanValue.of;
+import static com.google.gcloud.datastore.DateTimeValue.of;
+import static com.google.gcloud.datastore.DoubleValue.of;
+import static com.google.gcloud.datastore.EntityValue.of;
+import static com.google.gcloud.datastore.KeyValue.of;
+import static com.google.gcloud.datastore.ListValue.of;
+import static com.google.gcloud.datastore.LongValue.of;
+import static com.google.gcloud.datastore.NullValue.of;
+import static com.google.gcloud.datastore.StringValue.of;
+
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.common.collect.ImmutableSortedMap;
 import com.google.gcloud.datastore.Value.Type;
@@ -58,57 +69,57 @@ public B set(String name, Value value) {
     }
 
     public B setNull(String name) {
-      properties.put(name, new NullValue());
+      properties.put(name, of());
       return self();
     }
 
     public B set(String name, String value) {
-      properties.put(name, new StringValue(value));
+      properties.put(name, of(value));
       return self();
     }
 
     public B set(String name, long value) {
-      properties.put(name, new LongValue(value));
+      properties.put(name, of(value));
       return self();
     }
 
     public B set(String name, double value) {
-      properties.put(name, new DoubleValue(value));
+      properties.put(name, of(value));
       return self();
     }
 
     public B set(String name, boolean value) {
-      properties.put(name, new BooleanValue(value));
+      properties.put(name, of(value));
       return self();
     }
 
     public B set(String name, DateTime value) {
-      properties.put(name, new DateTimeValue(value));
+      properties.put(name, of(value));
       return self();
     }
 
     public B set(String name, Key value) {
-      properties.put(name, new KeyValue(value));
+      properties.put(name, of(value));
       return self();
     }
 
     public B set(String name, PartialEntity value) {
-      properties.put(name, new EntityValue(value));
+      properties.put(name, of(value));
       return self();
     }
 
     public B set(String name, List> values) {
-      properties.put(name, new ListValue(values));
+      properties.put(name, of(values));
       return self();
     }
 
     public B set(String name, Value... value) {
-      properties.put(name, new ListValue(Arrays.asList(value)));
+      properties.put(name, of(Arrays.asList(value)));
       return self();
     }
 
     public B set(String name, Blob value) {
-      properties.put(name, new BlobValue(value));
+      properties.put(name, of(value));
       return self();
     }
 
diff --git a/src/main/java/com/google/gcloud/datastore/BlobValue.java b/src/main/java/com/google/gcloud/datastore/BlobValue.java
index 8d369c9eb447..73671e167657 100644
--- a/src/main/java/com/google/gcloud/datastore/BlobValue.java
+++ b/src/main/java/com/google/gcloud/datastore/BlobValue.java
@@ -57,6 +57,10 @@ public Builder toBuilder() {
     return new Builder().mergeFrom(this);
   }
 
+  public static BlobValue of(Blob blob) {
+    return new BlobValue(blob);
+  }
+
   public static Builder builder(Blob blob) {
     return new Builder().set(blob);
   }
diff --git a/src/main/java/com/google/gcloud/datastore/BooleanValue.java b/src/main/java/com/google/gcloud/datastore/BooleanValue.java
index f6323bb3f794..86e080b096fb 100644
--- a/src/main/java/com/google/gcloud/datastore/BooleanValue.java
+++ b/src/main/java/com/google/gcloud/datastore/BooleanValue.java
@@ -57,6 +57,10 @@ public Builder toBuilder() {
     return new Builder().mergeFrom(this);
   }
 
+  public static BooleanValue of(boolean value) {
+    return new BooleanValue(value);
+  }
+
   public static Builder builder(boolean value) {
     return new Builder().set(value);
   }
diff --git a/src/main/java/com/google/gcloud/datastore/DateTimeValue.java b/src/main/java/com/google/gcloud/datastore/DateTimeValue.java
index a178c71a9665..fff1b35d7f29 100644
--- a/src/main/java/com/google/gcloud/datastore/DateTimeValue.java
+++ b/src/main/java/com/google/gcloud/datastore/DateTimeValue.java
@@ -58,6 +58,10 @@ public Builder toBuilder() {
     return new Builder().mergeFrom(this);
   }
 
+  public static DateTimeValue of(DateTime dateTime) {
+    return new DateTimeValue(dateTime);
+  }
+
   public static Builder builder(DateTime dateTime) {
     return new Builder().set(dateTime);
   }
diff --git a/src/main/java/com/google/gcloud/datastore/DoubleValue.java b/src/main/java/com/google/gcloud/datastore/DoubleValue.java
index 749a2a9b7bd5..5d5b510739f0 100644
--- a/src/main/java/com/google/gcloud/datastore/DoubleValue.java
+++ b/src/main/java/com/google/gcloud/datastore/DoubleValue.java
@@ -57,6 +57,10 @@ public Builder toBuilder() {
     return new Builder().mergeFrom(this);
   }
 
+  public static DoubleValue of(double value) {
+    return new DoubleValue(value);
+  }
+
   public static Builder builder(double value) {
     return new Builder().set(value);
   }
diff --git a/src/main/java/com/google/gcloud/datastore/EntityValue.java b/src/main/java/com/google/gcloud/datastore/EntityValue.java
index 243c1bac7be9..061cf6c57fa5 100644
--- a/src/main/java/com/google/gcloud/datastore/EntityValue.java
+++ b/src/main/java/com/google/gcloud/datastore/EntityValue.java
@@ -66,6 +66,10 @@ public Builder toBuilder() {
     return new Builder().mergeFrom(this);
   }
 
+  public static EntityValue of(PartialEntity entity) {
+    return new EntityValue(entity);
+  }
+
   public static Builder builder(PartialEntity entity) {
     return new Builder().set(entity).indexed(false);
   }
diff --git a/src/main/java/com/google/gcloud/datastore/KeyValue.java b/src/main/java/com/google/gcloud/datastore/KeyValue.java
index b9aab3134a86..a97399b5006c 100644
--- a/src/main/java/com/google/gcloud/datastore/KeyValue.java
+++ b/src/main/java/com/google/gcloud/datastore/KeyValue.java
@@ -57,6 +57,10 @@ public Builder toBuilder() {
     return new Builder().mergeFrom(this);
   }
 
+  public static KeyValue of(Key key) {
+    return new KeyValue(key);
+  }
+
   public static Builder builder(Key key) {
     return new Builder().set(key);
   }
diff --git a/src/main/java/com/google/gcloud/datastore/ListValue.java b/src/main/java/com/google/gcloud/datastore/ListValue.java
index b9d7aa66c21c..5af230d768b1 100644
--- a/src/main/java/com/google/gcloud/datastore/ListValue.java
+++ b/src/main/java/com/google/gcloud/datastore/ListValue.java
@@ -116,6 +116,14 @@ public Builder toBuilder() {
     return new Builder().mergeFrom(this);
   }
 
+  public static ListValue of(List> values) {
+    return new ListValue(values);
+  }
+
+  public static ListValue of(Value first, Value... other) {
+    return new ListValue(first, other);
+  }
+
   public static Builder builder() {
     return new Builder();
   }
diff --git a/src/main/java/com/google/gcloud/datastore/LongValue.java b/src/main/java/com/google/gcloud/datastore/LongValue.java
index 9107223c7af4..4ab977ed96da 100644
--- a/src/main/java/com/google/gcloud/datastore/LongValue.java
+++ b/src/main/java/com/google/gcloud/datastore/LongValue.java
@@ -57,6 +57,10 @@ public Builder toBuilder() {
     return new Builder().mergeFrom(this);
   }
 
+  public static LongValue of(long value) {
+    return new LongValue(value);
+  }
+
   public static Builder builder(long value) {
     return new Builder().set(value);
   }
diff --git a/src/main/java/com/google/gcloud/datastore/NullValue.java b/src/main/java/com/google/gcloud/datastore/NullValue.java
index cf4f463095d0..af81c017bc72 100644
--- a/src/main/java/com/google/gcloud/datastore/NullValue.java
+++ b/src/main/java/com/google/gcloud/datastore/NullValue.java
@@ -63,6 +63,10 @@ public Builder toBuilder() {
     return new Builder().mergeFrom(this);
   }
 
+  public static NullValue of() {
+    return new NullValue();
+  }
+
   public static Builder builder() {
     return new Builder();
   }
diff --git a/src/main/java/com/google/gcloud/datastore/RawValue.java b/src/main/java/com/google/gcloud/datastore/RawValue.java
index cf5852d46c13..d5fc455df4a9 100644
--- a/src/main/java/com/google/gcloud/datastore/RawValue.java
+++ b/src/main/java/com/google/gcloud/datastore/RawValue.java
@@ -55,6 +55,10 @@ public Builder toBuilder() {
     return new Builder().mergeFrom(this);
   }
 
+  static RawValue of(DatastoreV1.Value valuePb) {
+    return new RawValue(valuePb);
+  }
+
   static Builder builder(DatastoreV1.Value valuePb) {
     Builder builder = new Builder();
     if (valuePb.hasIndexed()) {
diff --git a/src/main/java/com/google/gcloud/datastore/StringValue.java b/src/main/java/com/google/gcloud/datastore/StringValue.java
index 6434b6a86f40..dad18654c8bd 100644
--- a/src/main/java/com/google/gcloud/datastore/StringValue.java
+++ b/src/main/java/com/google/gcloud/datastore/StringValue.java
@@ -61,6 +61,10 @@ public Builder toBuilder() {
     return new Builder().mergeFrom(this);
   }
 
+  public static StringValue of(String value) {
+    return new StringValue(value);
+  }
+
   public static Builder builder(String value) {
     return new Builder().set(value);
   }
diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index b5e6a98fe1dc..77ee46e1438b 100644
--- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -1,6 +1,13 @@
 package com.google.gcloud.datastore;
 
 import static com.google.api.client.util.Preconditions.checkNotNull;
+import static com.google.gcloud.datastore.BlobValue.of;
+import static com.google.gcloud.datastore.BooleanValue.of;
+import static com.google.gcloud.datastore.DateTimeValue.of;
+import static com.google.gcloud.datastore.DoubleValue.of;
+import static com.google.gcloud.datastore.KeyValue.of;
+import static com.google.gcloud.datastore.LongValue.of;
+import static com.google.gcloud.datastore.StringValue.of;
 
 import com.google.api.client.util.Preconditions;
 import com.google.api.services.datastore.DatastoreV1;
@@ -51,6 +58,7 @@ public enum Operator {
       GREATER_THAN_OR_EQUAL,
       EQUAL,
       HAS_ANCESTOR,
+      IS_NULL,
       AND
     }
 
@@ -85,6 +93,34 @@ public static Filter le(String property, Value value) {
       return new Filter(property, Operator.LESS_THAN, value, null);
     }
 
+    public static Filter le(String property, String value) {
+      return new Filter(property, Operator.LESS_THAN, of(value), null);
+    }
+
+    public static Filter le(String property, long value) {
+      return new Filter(property, Operator.LESS_THAN, of(value), null);
+    }
+
+    public static Filter le(String property, double value) {
+      return new Filter(property, Operator.LESS_THAN, of(value), null);
+    }
+
+    public static Filter le(String property, boolean value) {
+      return new Filter(property, Operator.LESS_THAN, of(value), null);
+    }
+
+    public static Filter le(String property, DateTime value) {
+      return new Filter(property, Operator.LESS_THAN, of(value), null);
+    }
+
+    public static Filter le(String property, Key value) {
+      return new Filter(property, Operator.LESS_THAN, of(value), null);
+    }
+
+    public static Filter le(String property, Blob value) {
+      return new Filter(property, Operator.LESS_THAN, of(value), null);
+    }
+
     public static Filter lte(String property, Value value) {
       return new Filter(property, Operator.LESS_THAN_OR_EQUAL, value, null);
     }
@@ -105,6 +141,10 @@ public static Filter hasAncestor(String property) {
       return new Filter(property, Operator.HAS_ANCESTOR, null, null);
     }
 
+    public static Filter isNull(String property) {
+      return new Filter(property, Operator.IS_NULL, null, null);
+    }
+
     public static Filter and(Filter first, Filter... other) {
       Set combined = new LinkedHashSet<>();
       for (Filter f : Iterables.concat(Collections.singleton(first), Arrays.asList(other))) {
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
index a9fe6e53c005..a1cd276e4be7 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
@@ -21,8 +21,8 @@ public class DatastoreServiceTest {
   private static final String DATASET = "dataset1";
   private static final String KIND1 = "kind1";
   private static final String KIND2 = "kind2";
-  private static final NullValue NULL_VALUE = new NullValue();
-  private static final StringValue STR_VALUE = new StringValue("str");
+  private static final NullValue NULL_VALUE = NullValue.of();
+  private static final StringValue STR_VALUE = StringValue.of("str");
   private static final BooleanValue BOOL_VALUE = BooleanValue.builder(false).indexed(false).build();
   private static final PartialKey PARTIAL_KEY1 = PartialKey.builder(DATASET, KIND1).build();
   private static final PartialKey PARTIAL_KEY2 = PartialKey.builder(DATASET, KIND2).build();
@@ -31,18 +31,18 @@ public class DatastoreServiceTest {
   private static final Key KEY3 = Key.builder(KEY2).name("bla").build();
   private static final Key KEY4 = KEY2.newKey("newName1");
   private static final Key KEY5 = KEY2.newKey("newName2");
-  private static final KeyValue KEY_VALUE = new KeyValue(KEY1);
+  private static final KeyValue KEY_VALUE = KeyValue.of(KEY1);
   private static final ListValue LIST_VALUE1 = ListValue.builder()
       .addValue(NULL_VALUE)
       .addValue(STR_VALUE, BOOL_VALUE)
       .build();
-  private static final ListValue LIST_VALUE2 = new ListValue(Collections.singletonList(KEY_VALUE));
+  private static final ListValue LIST_VALUE2 = ListValue.of(Collections.singletonList(KEY_VALUE));
   private static final PartialEntity PARTIAL_ENTITY1 = PartialEntity.builder(PARTIAL_KEY2)
       .set("str", STR_VALUE).set("bool", BOOL_VALUE).set("list", LIST_VALUE1).build();
   private static final PartialEntity PARTIAL_ENTITY2 = PartialEntity.builder(PARTIAL_ENTITY1)
       .remove("str").set("bool", true).set("list", LIST_VALUE1.get()).build();
   private static final Entity ENTITY1 = Entity.builder(KEY1).set("str", STR_VALUE)
-      .set("bool", BOOL_VALUE).set("partial1", new EntityValue(PARTIAL_ENTITY1))
+      .set("bool", BOOL_VALUE).set("partial1", EntityValue.of(PARTIAL_ENTITY1))
       .set("list", LIST_VALUE2).build();
   private static final Entity ENTITY2 = Entity.builder(ENTITY1).key(KEY2).remove("str")
       .setNull("null").build();
@@ -143,7 +143,7 @@ public void testNewTransactionRollback() {
     Transaction transaction = datastore.newTransaction();
     transaction.add(ENTITY3);
     Entity entity2 = Entity.builder(ENTITY2).clear().setNull("bla")
-        .set("list3", new StringValue("bla"), StringValue.builder("bla").build()).build();
+        .set("list3", StringValue.of("bla"), StringValue.builder("bla").build()).build();
     transaction.update(entity2);
     transaction.delete(KEY1);
     transaction.rollback();
@@ -200,7 +200,7 @@ public void testNewBatchWriter() {
     BatchWriter batchWriter = datastore.newBatchWriter();
     Entity entity1 = Entity.builder(ENTITY1).clear().build();
     Entity entity2 = Entity.builder(ENTITY2).clear().setNull("bla").build();
-    Entity entity4 = Entity.builder(KEY4).set("value", new StringValue("value")).build();
+    Entity entity4 = Entity.builder(KEY4).set("value", StringValue.of("value")).build();
     Entity entity5 = Entity.builder(KEY5).set("value", "value").build();
     batchWriter.add(entity4, entity5);
     batchWriter.put(ENTITY3, entity1, entity2);
diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
index eb63574ec021..ce3ea54f0a85 100644
--- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java
+++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
@@ -40,20 +40,19 @@ public class SerializationTest {
       .addArgument(20)
       .namespace("ns1")
       .build();
-  private static final KeyValue KEY_VALUE = new KeyValue(KEY1);
+  private static final KeyValue KEY_VALUE = KeyValue.of(KEY1);
   private static final NullValue NULL_VALUE = NullValue.builder().indexed(true).build();
-  private static final StringValue STRING_VALUE = new StringValue("hello");
-  private static final LongValue LONG_VALUE = new LongValue(123);
-  private static final DoubleValue DOUBLE_VALUE = new DoubleValue(12.34);
-  private static final BooleanValue BOOLEAN_VALUE = new BooleanValue(true);
-  private static final DateTimeValue DATE_AND_TIME_VALUE =
-      new DateTimeValue(DateTime.now());
-  private static final BlobValue BLOB_VALUE = new BlobValue(BLOB1);
-  private static final RawValue RAW_VALUE = new RawValue(
+  private static final StringValue STRING_VALUE = StringValue.of("hello");
+  private static final LongValue LONG_VALUE = LongValue.of(123);
+  private static final DoubleValue DOUBLE_VALUE = DoubleValue.of(12.34);
+  private static final BooleanValue BOOLEAN_VALUE = BooleanValue.of(true);
+  private static final DateTimeValue DATE_AND_TIME_VALUE = DateTimeValue.of(DateTime.now());
+  private static final BlobValue BLOB_VALUE = BlobValue.of(BLOB1);
+  private static final RawValue RAW_VALUE = RawValue.of(
       DatastoreV1.Value.newBuilder().setBlobKeyValue("blob-key").setMeaning(18).build());
   private static final Entity ENTITY1 = Entity.builder(KEY1).build();
   private static final Entity ENTITY2 =
-      Entity.builder(KEY2).set("null", new NullValue()).build();
+      Entity.builder(KEY2).set("null", NullValue.of()).build();
   private static final Entity ENTITY3 = Entity.builder(KEY2)
       .set("p1", StringValue.builder("hi1").meaning(10).build())
       .set("p2", StringValue.builder("hi2").meaning(11).indexed(false).build())
@@ -66,12 +65,9 @@ public class SerializationTest {
       .set("p1", STRING_VALUE)
       .set("p2", LongValue.builder(100).indexed(false).meaning(100).build())
       .build();
-  private static final EntityValue EMBEDDED_ENTITY_VALUE1 =
-      new EntityValue(EMBEDDED_ENTITY1);
-  private static final EntityValue EMBEDDED_ENTITY_VALUE2 =
-      new EntityValue(EMBEDDED_ENTITY2);
-  private static final EntityValue EMBEDDED_ENTITY_VALUE3 =
-      new EntityValue(EMBEDDED_ENTITY3);
+  private static final EntityValue EMBEDDED_ENTITY_VALUE1 = EntityValue.of(EMBEDDED_ENTITY1);
+  private static final EntityValue EMBEDDED_ENTITY_VALUE2 = EntityValue.of(EMBEDDED_ENTITY2);
+  private static final EntityValue EMBEDDED_ENTITY_VALUE3 = EntityValue.of(EMBEDDED_ENTITY3);
   private static final ListValue LIST_VALUE = ListValue.builder()
       .addValue(NULL_VALUE)
       .addValue(STRING_VALUE)

From 761fd07121353f2f9375ddb3f39a2b367b2c1fa1 Mon Sep 17 00:00:00 2001
From: aozarov 
Date: Fri, 12 Dec 2014 17:38:54 -0800
Subject: [PATCH 052/771] work in progress

---
 .classpath                                    |  10 +
 .settings/org.eclipse.jdt.core.prefs          |   3 +
 .../com/google/gcloud/datastore/BaseKey.java  |  11 +-
 .../gcloud/datastore/BatchWriterImpl.java     |   2 +-
 .../gcloud/datastore/DatastoreService.java    |   2 +-
 .../datastore/DatastoreServiceImpl.java       |  17 +-
 .../datastore/DatastoreServiceOptions.java    |  28 +-
 .../com/google/gcloud/datastore/GqlQuery.java |  41 ++-
 .../google/gcloud/datastore/KeyBuilder.java   |   4 +-
 .../com/google/gcloud/datastore/Query.java    |  15 +-
 .../gcloud/datastore/QueryResultImpl.java     |  42 ++-
 .../gcloud/datastore/StructuredQuery.java     | 295 ++++++++++++++----
 .../google/gcloud/datastore/Validator.java    |  44 +++
 .../datastore/DatastoreServiceTest.java       |   2 +-
 14 files changed, 357 insertions(+), 159 deletions(-)
 create mode 100644 src/main/java/com/google/gcloud/datastore/Validator.java

diff --git a/.classpath b/.classpath
index 9ed6ee4d0713..d6cf6121af66 100644
--- a/.classpath
+++ b/.classpath
@@ -23,5 +23,15 @@
 		
 	
 	
+	
+		
+			
+		
+	
+	
+		
+			
+		
+	
 	
 
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index 0192f1f052aa..62f72418e536 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -8,6 +8,8 @@ org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonN
 org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
 org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
 org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.compliance=1.7
 org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
 org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
 org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
@@ -90,3 +92,4 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
 org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning
 org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
 org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/src/main/java/com/google/gcloud/datastore/BaseKey.java b/src/main/java/com/google/gcloud/datastore/BaseKey.java
index 4b4a33aa404e..2812574f6dbe 100644
--- a/src/main/java/com/google/gcloud/datastore/BaseKey.java
+++ b/src/main/java/com/google/gcloud/datastore/BaseKey.java
@@ -1,8 +1,9 @@
 package com.google.gcloud.datastore;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gcloud.datastore.DatastoreServiceOptions.validateDataset;
-import static com.google.gcloud.datastore.DatastoreServiceOptions.validateNamespace;
+import static com.google.gcloud.datastore.Validator.validateDataset;
+import static com.google.gcloud.datastore.Validator.validateKind;
+import static com.google.gcloud.datastore.Validator.validateNamespace;
 
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.common.base.Preconditions;
@@ -84,12 +85,6 @@ public B kind(String kind) {
       return self();
     }
 
-    private String validateKind(String kind) {
-      checkArgument(!Strings.isNullOrEmpty(kind), "kind must not be empty or null");
-      checkArgument(kind.length() <= 500, "kind must not contain more than 500 characters");
-      return kind;
-    }
-
     public B clearPath() {
       ancestors.clear();
       return self();
diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
index 0564d9b18c30..2395c2968c54 100644
--- a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
@@ -27,7 +27,7 @@ class BatchWriterImpl implements BatchWriter {
     if (optionsMap.containsKey(ForceWrites.class)) {
       force = ((ForceWrites) optionsMap.get(ForceWrites.class)).force();
     } else {
-      force = datastore.getOptions().force();
+      force = datastore.options().force();
     }
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java
index b7633c49cd10..efd41bd9544e 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java
@@ -10,7 +10,7 @@ public interface DatastoreService extends DatastoreReader, DatastoreWriter {
   /**
    * Returns the {@code DatastoreServiceOptions} for this service.
    */
-  DatastoreServiceOptions getOptions();
+  DatastoreServiceOptions options();
 
   /**
    * Returns a key builder for the requested {@code kind}.
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
index 8fe92407260f..d55aaa47a380 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
@@ -28,7 +28,7 @@ final class DatastoreServiceImpl implements DatastoreService {
   }
 
   @Override
-  public DatastoreServiceOptions getOptions() {
+  public DatastoreServiceOptions options() {
     return options;
   }
 
@@ -53,20 +53,7 @@ public  QueryResult runQuery(Query query) {
   }
 
    QueryResult runQuery(DatastoreV1.ReadOptions readOptionsPb, Query query) {
-    DatastoreV1.RunQueryRequest.Builder requestPbBuilder = DatastoreV1.RunQueryRequest.newBuilder();
-    if (readOptionsPb != null) {
-      requestPbBuilder.setReadOptions(readOptionsPb);
-    }
-    DatastoreV1.PartitionId.Builder partitionIdPb = DatastoreV1.PartitionId.newBuilder();
-    partitionIdPb.setDatasetId(options.dataset());
-    String namespace = query.namespace() != null ? query.namespace() : options.namespace();
-    if (namespace != null) {
-      partitionIdPb.setNamespace(namespace);
-    }
-    requestPbBuilder.setPartitionId(partitionIdPb.build());
-    query.populatePb(requestPbBuilder, 0, null);
-    DatastoreV1.RunQueryRequest requestPb = requestPbBuilder.build();
-    return new QueryResultImpl<>(this, query, requestPb, runQuery(requestPb).getBatch());
+    return new QueryResultImpl<>(this, readOptionsPb, query);
   }
 
   DatastoreV1.RunQueryResponse runQuery(DatastoreV1.RunQueryRequest requestPb) {
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java
index 2f41df15a855..c0d5e3aee3a1 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java
@@ -2,27 +2,21 @@
 
 import static com.google.common.base.MoreObjects.firstNonNull;
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.gcloud.datastore.Validator.validateDataset;
+import static com.google.gcloud.datastore.Validator.validateNamespace;
 
 import com.google.api.services.datastore.client.DatastoreOptions;
-import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import com.google.gcloud.ServiceOptions;
 
 import java.lang.reflect.Method;
 import java.util.Set;
-import java.util.regex.Pattern;
 
 public class DatastoreServiceOptions extends ServiceOptions {
 
   private static final String DATASTORE_SCOPE = "https://www.googleapis.com/auth/datastore";
   private static final String USERINFO_SCOPE = "https://www.googleapis.com/auth/userinfo.email";
   private static final Set SCOPES = ImmutableSet.of(DATASTORE_SCOPE, USERINFO_SCOPE);
-  private static final Pattern DATASET_PATTERN = Pattern.compile(
-      "([a-z\\d\\-]{1,100}~)?([a-z\\d][a-z\\d\\-\\.]{0,99}\\:)?([a-z\\d][a-z\\d\\-]{0,99})");
-  private static final int MAX_NAMESPACE_LENGTH = 100;
-  private static final Pattern NAMESPACE_PATTERN =
-      Pattern.compile(String.format("[0-9A-Za-z\\._\\-]{0,%d}", MAX_NAMESPACE_LENGTH));
-
   private final String dataset;
   private final String namespace;
   private final boolean force;
@@ -79,24 +73,6 @@ public String namespace() {
     return namespace;
   }
 
-  static String validateDataset(String dataset) {
-    checkArgument(!Strings.isNullOrEmpty(dataset), "dataset can't be empty or null");
-    checkArgument(DATASET_PATTERN.matcher(dataset).matches(),
-          "dataset must match the following pattern: " + DATASET_PATTERN.pattern());
-    return dataset;
-  }
-
-  static String validateNamespace(String namespace) {
-    if (namespace != null) {
-      checkArgument(!namespace.isEmpty(), "namespace must not be an empty string");
-      checkArgument(namespace.length() <= 100,
-          "namespace must not contain more than 100 characters");
-      checkArgument(NAMESPACE_PATTERN.matcher(namespace).matches(),
-          "namespace must the following pattern: " + NAMESPACE_PATTERN.pattern());
-    }
-    return namespace;
-  }
-
   private static String defaultNamespace() {
     // TODO(ozarov): An alternative to reflection would be to depend on AE api jar:
     // http://mvnrepository.com/artifact/com.google.appengine/appengine-api-1.0-sdk/1.2.0
diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
index d7c481d613ee..a1ffa9f6ff70 100644
--- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
@@ -1,6 +1,7 @@
 package com.google.gcloud.datastore;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.gcloud.datastore.Validator.validateNamespace;
 
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.common.base.MoreObjects;
@@ -10,7 +11,6 @@
 import com.google.common.primitives.Booleans;
 import com.google.common.primitives.Doubles;
 import com.google.common.primitives.Longs;
-import com.google.protobuf.ByteString;
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import java.util.ArrayList;
@@ -134,7 +134,7 @@ public Builder query(String query) {
     }
 
     public Builder namespace(String namespace) {
-      this.namespace = namespace;
+      this.namespace = validateNamespace(namespace);
       return this;
     }
 
@@ -239,15 +239,15 @@ public Builder addArgument(Blob... value) {
       return this;
     }
 
+    public GqlQuery build() {
+      return new GqlQuery<>(this);
+    }
+
     @SuppressWarnings("rawtypes")
     private static Argument toArgument(Value.BuilderFactory builderFactory, List values) {
       return toArgument(null, builderFactory, values);
     }
 
-    public GqlQuery build() {
-      return new GqlQuery<>(this);
-    }
-
     @SuppressWarnings({"unchecked", "rawtypes"})
     private static Argument toArgument(String name, Value.BuilderFactory builderFactory,
         List values) {
@@ -341,14 +341,29 @@ protected DatastoreV1.GqlQuery toPb() {
   }
 
   @Override
-  protected void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb, int totalRead,
-      ByteString batchCursor) {
-    if (batchCursor == null) {
-      requestPb.setGqlQuery(toPb());
-      return;
-    }
-    // see b/18705483
+  protected void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb) {
+    requestPb.setGqlQuery(toPb());
+  }
+
+  @Override
+  protected GqlQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) {
     throw new UnsupportedOperationException("paging not implemented yet");
+    /*
+    // TODO: THIS IS A MAJOR HACK, remove when possible. see b/18705483
+    String PREFIX_GROUP = "\\s*(?SELECT .*?)";
+    String OFFSET_GROUP =
+        "(\\s+OFFSET\\s+(?[^\\s]+)(\\s+\\+\\s+(?[^\\s]+))?)?";
+    String LIMIT_GROUP =
+        "(\\s+LIMIT\\s+((?[^\\s]+)|FIRST \\((?[^,]+,[^\\)]+)\\)))?\\s*";
+    Pattern pattern =
+        Pattern.compile(PREFIX_GROUP + OFFSET_GROUP + LIMIT_GROUP, Pattern.CASE_INSENSITIVE);
+
+    Matcher matcher = pattern.matcher(queryString);
+
+    if (!matcher.matches()) {
+      throw new UnsupportedOperationException("paging for this query is not implemented yet");
+    }
+    */
   }
 
   @Override
diff --git a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java
index 6860063124d0..a906a29d423c 100644
--- a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java
+++ b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java
@@ -16,9 +16,9 @@ public final class KeyBuilder extends BaseKey.Builder {
    * Constructing a KeyBuilder.
    */
   public KeyBuilder(DatastoreService service, String kind) {
-    super(checkNotNull(service).getOptions().dataset(), kind);
+    super(checkNotNull(service).options().dataset(), kind);
     this.service = service;
-    namespace(service.getOptions().namespace());
+    namespace(service.options().namespace());
   }
 
   @Override
diff --git a/src/main/java/com/google/gcloud/datastore/Query.java b/src/main/java/com/google/gcloud/datastore/Query.java
index d1c29d189599..bf474bc73d1e 100644
--- a/src/main/java/com/google/gcloud/datastore/Query.java
+++ b/src/main/java/com/google/gcloud/datastore/Query.java
@@ -1,10 +1,11 @@
 package com.google.gcloud.datastore;
 
+import static com.google.api.client.util.Preconditions.checkNotNull;
+
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.MoreObjects.ToStringHelper;
 import com.google.gcloud.datastore.QueryResult.Type;
-import com.google.protobuf.ByteString;
 import com.google.protobuf.GeneratedMessage;
 import com.google.protobuf.InvalidProtocolBufferException;
 
@@ -67,7 +68,7 @@ public static ResultType keyOnly() {
   }
 
   Query(ResultType resultType, String namespace) {
-    this.resultType = resultType;
+    this.resultType = checkNotNull(resultType);
     this.namespace = namespace;
   }
 
@@ -95,13 +96,7 @@ protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException {
   protected abstract Object fromPb(ResultType resultType, String namespace, byte[] bytesPb)
       throws InvalidProtocolBufferException;
 
-  protected abstract void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb, int totalRead,
-      ByteString batchCursor);
+  protected abstract void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb);
 
-  /**
-   * Returns a new structured query builder.
-   */
-  public static StructuredQuery.Builder builder() {
-    return new StructuredQuery.Builder();
-  }
+  protected abstract Query nextQuery(DatastoreV1.QueryResultBatch responsePb);
 }
diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
index f24130212ab8..734b8b1bee4a 100644
--- a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
@@ -13,9 +13,10 @@ class QueryResultImpl implements QueryResult {
       RESULT_TYPE_CONVERTER;
 
   private final DatastoreServiceImpl datastore;
-  private final Query query;
+  private final DatastoreV1.ReadOptions readOptionsPb;
   private final QueryResult.Type type;
-  private DatastoreV1.RunQueryRequest requestPb;
+  private Query query;
+  private DatastoreV1.QueryResultBatch resultPb;
   private Iterator entityResultPbIter;
   private ByteString endCursor;
   private int count;
@@ -29,37 +30,52 @@ class QueryResultImpl implements QueryResult {
     RESULT_TYPE_CONVERTER = builder.build();
   }
 
-  QueryResultImpl(DatastoreServiceImpl datastore, Query query,
-      DatastoreV1.RunQueryRequest requestPb, DatastoreV1.QueryResultBatch resultPb) {
+  QueryResultImpl(DatastoreServiceImpl datastore, DatastoreV1.ReadOptions readOptionsPb,
+      Query query) {
     this.datastore = datastore;
+    this.readOptionsPb = readOptionsPb;
     this.query = query;
-    this.requestPb = requestPb;
+    sendRequest();
     type = RESULT_TYPE_CONVERTER.get(resultPb.getEntityResultType());
     Preconditions.checkState(query.resultType().getType() == null
         || query.resultType().getType() == type, "Unexpected result type");
   }
 
-  void setQueryResultBatch(DatastoreV1.QueryResultBatch resultPb) {
+  private DatastoreV1.QueryResultBatch sendRequest() {
+    DatastoreV1.RunQueryRequest.Builder requestPb = DatastoreV1.RunQueryRequest.newBuilder();
+    if (readOptionsPb != null) {
+      requestPb.setReadOptions(readOptionsPb);
+    }
+    DatastoreV1.PartitionId.Builder partitionIdPb = DatastoreV1.PartitionId.newBuilder();
+    partitionIdPb.setDatasetId(datastore.options().dataset());
+    String namespace = query.namespace() != null
+        ? query.namespace()
+        : datastore.options().namespace();
+    if (namespace != null) {
+      partitionIdPb.setNamespace(namespace);
+    }
+    requestPb.setPartitionId(partitionIdPb.build());
+    query.populatePb(requestPb);
+    resultPb = datastore.runQuery(requestPb.build()).getBatch();
     entityResultPbIter = resultPb.getEntityResultList().iterator();
     if (DatastoreV1.QueryResultBatch.MoreResultsType.NOT_FINISHED == resultPb.getMoreResults()) {
       endCursor = resultPb.getEndCursor();
+    } else {
+      endCursor = null;
     }
+    return resultPb;
   }
 
   @Override
   public boolean hasNext() {
-    return entityResultPbIter.hasNext()  || endCursor != null;
+    return entityResultPbIter.hasNext() || endCursor != null;
   }
 
   @Override
   public T next() {
     if (!hasNext() && endCursor != null) {
-      DatastoreV1.RunQueryRequest.Builder requestPbBuilder = requestPb.toBuilder();
-      query.populatePb(requestPbBuilder, count, endCursor);
-      DatastoreV1.RunQueryRequest newRequestPb = requestPbBuilder.build();
-      DatastoreV1.RunQueryResponse responsePb = datastore.runQuery(newRequestPb);
-      requestPb = newRequestPb;
-      setQueryResultBatch(responsePb.getBatch());
+      query = query.nextQuery(resultPb);
+      sendRequest();
     }
     DatastoreV1.Entity entity = entityResultPbIter.next().getEntity();
     count++;
diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index 77ee46e1438b..8c172b717051 100644
--- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -12,29 +12,26 @@
 import com.google.api.client.util.Preconditions;
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
-import com.google.protobuf.ByteString;
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import java.io.Serializable;
 import java.util.ArrayDeque;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
-public final class StructuredQuery extends Query {
+public class StructuredQuery extends Query {
 
   private static final long serialVersionUID = 546838955624019594L;
 
   private final transient boolean keysOnly;
   private final transient String kind;
-  private final transient ImmutableList projection;
   private final transient Filter filter;
-  private final transient ImmutableList groupBy;
   private final transient ImmutableList orderBy;
   private final transient Cursor startCursor;
   private final transient Cursor endCursor;
@@ -42,7 +39,33 @@ public final class StructuredQuery extends Query {
   private final transient Integer limit;
 
 
-  public static final class Filter implements Serializable {
+  public static class Expression implements Serializable {
+
+    private static final long serialVersionUID = -6443285436239990860L;
+
+    private final Operator operator = null;
+    private final List values = null;
+
+    enum Operator {
+      AND,
+      OR
+    }
+
+    /*
+    Expression(Operator operator, Expression first, Expression... other) {
+      this.operator = operator;
+      ImmutableList.builder().
+      this.
+    }
+    */
+  }
+
+/*
+  public static final class And extends Expression {
+
+  }
+*/
+  public static final class Filter extends Expression {
 
     private static final long serialVersionUID = -4514695915258598597L;
 
@@ -51,7 +74,7 @@ public static final class Filter implements Serializable {
     private final Value value;
     private final Filter next;
 
-    public enum Operator {
+    enum Operator {
       LESS_THAN,
       LESS_THAN_OR_EQUAL,
       GREATER_THAN,
@@ -62,10 +85,24 @@ public enum Operator {
       AND
     }
 
-    private Filter(String property, Operator operator, Value value, Filter next) {
+    private Filter(String property, Operator operator, Value value) {
+      this.property = checkNotNull(property);
+      this.operator = checkNotNull(operator);
+      this.value = checkNotNull(value);
+      this.next = null;
+    }
+
+    private Filter(String property, Operator operator) {
       this.property = checkNotNull(property);
       this.operator = checkNotNull(operator);
-      this.value = value;
+      this.value = null;
+      this.next = null;
+    }
+
+    private Filter(Filter from, Filter next) {
+      this.property = from.property;
+      this.operator = from.operator;
+      this.value = from.value;
       this.next = next;
     }
 
@@ -90,66 +127,178 @@ public boolean equals(Object obj) {
     }
 
     public static Filter le(String property, Value value) {
-      return new Filter(property, Operator.LESS_THAN, value, null);
+      return new Filter(property, Operator.LESS_THAN, value);
     }
 
     public static Filter le(String property, String value) {
-      return new Filter(property, Operator.LESS_THAN, of(value), null);
+      return new Filter(property, Operator.LESS_THAN, of(value));
     }
 
     public static Filter le(String property, long value) {
-      return new Filter(property, Operator.LESS_THAN, of(value), null);
+      return new Filter(property, Operator.LESS_THAN, of(value));
     }
 
     public static Filter le(String property, double value) {
-      return new Filter(property, Operator.LESS_THAN, of(value), null);
+      return new Filter(property, Operator.LESS_THAN, of(value));
     }
 
     public static Filter le(String property, boolean value) {
-      return new Filter(property, Operator.LESS_THAN, of(value), null);
+      return new Filter(property, Operator.LESS_THAN, of(value));
     }
 
     public static Filter le(String property, DateTime value) {
-      return new Filter(property, Operator.LESS_THAN, of(value), null);
+      return new Filter(property, Operator.LESS_THAN, of(value));
     }
 
     public static Filter le(String property, Key value) {
-      return new Filter(property, Operator.LESS_THAN, of(value), null);
+      return new Filter(property, Operator.LESS_THAN, of(value));
     }
 
     public static Filter le(String property, Blob value) {
-      return new Filter(property, Operator.LESS_THAN, of(value), null);
+      return new Filter(property, Operator.LESS_THAN, of(value));
     }
 
     public static Filter lte(String property, Value value) {
-      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, value, null);
+      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, value);
+    }
+
+    public static Filter lte(String property, String value) {
+      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    }
+
+    public static Filter lte(String property, long value) {
+      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    }
+
+    public static Filter lte(String property, double value) {
+      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    }
+
+    public static Filter lte(String property, boolean value) {
+      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    }
+
+    public static Filter lte(String property, DateTime value) {
+      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    }
+
+    public static Filter lte(String property, Key value) {
+      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    }
+
+    public static Filter lte(String property, Blob value) {
+      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
     public static Filter gt(String property, Value value) {
-      return new Filter(property, Operator.GREATER_THAN, value, null);
+      return new Filter(property, Operator.GREATER_THAN, value);
+    }
+
+    public static Filter gt(String property, String value) {
+      return new Filter(property, Operator.GREATER_THAN, of(value));
+    }
+
+    public static Filter gt(String property, long value) {
+      return new Filter(property, Operator.GREATER_THAN, of(value));
+    }
+
+    public static Filter gt(String property, double value) {
+      return new Filter(property, Operator.GREATER_THAN, of(value));
+    }
+
+    public static Filter gt(String property, boolean value) {
+      return new Filter(property, Operator.GREATER_THAN, of(value));
+    }
+
+    public static Filter gt(String property, DateTime value) {
+      return new Filter(property, Operator.GREATER_THAN, of(value));
+    }
+
+    public static Filter gt(String property, Key value) {
+      return new Filter(property, Operator.GREATER_THAN, of(value));
+    }
+
+    public static Filter gt(String property, Blob value) {
+      return new Filter(property, Operator.GREATER_THAN, of(value));
     }
 
     public static Filter gte(String property, Value value) {
-      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, value, null);
+      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, value);
+    }
+
+    public static Filter gte(String property, String value) {
+      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    }
+
+    public static Filter gte(String property, long value) {
+      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    }
+
+    public static Filter gte(String property, double value) {
+      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    }
+
+    public static Filter gte(String property, boolean value) {
+      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    }
+
+    public static Filter gte(String property, DateTime value) {
+      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    }
+
+    public static Filter gte(String property, Key value) {
+      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    }
+
+    public static Filter gte(String property, Blob value) {
+      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
     public static Filter eq(String property, Value value) {
-      return new Filter(property, Operator.EQUAL, value, null);
+      return new Filter(property, Operator.EQUAL, value);
+    }
+
+    public static Filter eq(String property, String value) {
+      return new Filter(property, Operator.EQUAL, of(value));
+    }
+
+    public static Filter eq(String property, long value) {
+      return new Filter(property, Operator.EQUAL, of(value));
+    }
+
+    public static Filter eq(String property, double value) {
+      return new Filter(property, Operator.EQUAL, of(value));
+    }
+
+    public static Filter eq(String property, boolean value) {
+      return new Filter(property, Operator.EQUAL, of(value));
+    }
+
+    public static Filter eq(String property, DateTime value) {
+      return new Filter(property, Operator.EQUAL, of(value));
+    }
+
+    public static Filter eq(String property, Key value) {
+      return new Filter(property, Operator.EQUAL, of(value));
+    }
+
+    public static Filter eq(String property, Blob value) {
+      return new Filter(property, Operator.EQUAL, of(value));
     }
 
     public static Filter hasAncestor(String property) {
-      return new Filter(property, Operator.HAS_ANCESTOR, null, null);
+      return new Filter(property, Operator.HAS_ANCESTOR);
     }
 
     public static Filter isNull(String property) {
-      return new Filter(property, Operator.IS_NULL, null, null);
+      return new Filter(property, Operator.IS_NULL);
     }
 
     public static Filter and(Filter first, Filter... other) {
       Set combined = new LinkedHashSet<>();
       for (Filter f : Iterables.concat(Collections.singleton(first), Arrays.asList(other))) {
         while (f != null) {
-          combined.add(new Filter(f.property, f.operator, f.value, null));
+          combined.add(new Filter(f, null));
           f = f.next;
         }
       }
@@ -158,12 +307,12 @@ public static Filter and(Filter first, Filter... other) {
         stack.push(f);
       }
       while (true) {
-        Filter f1 = stack.pop();
+        Filter top = stack.pop();
         if (stack.isEmpty()) {
-          return f1;
+          return top;
         }
-        Filter f2 = stack.pop();
-        stack.push(new Filter(f2.property, f2.operator, f2.value, f1));
+        Filter beforeTop = stack.pop();
+        stack.push(new Filter(beforeTop, top));
       }
     }
   }
@@ -219,7 +368,7 @@ public static OrderBy desc(String property) {
     }
   }
 
-  static class Builder {
+  static class Builder> {
 
     private String namespace;
     private String kind;
@@ -227,62 +376,68 @@ static class Builder {
     private Cursor endCursor;
     private Integer offset;
     private Integer limit;
+    private Filter filter;
+    private List orderBy = new LinkedList<>();
 
-    public Builder namespace(String namespace) {
+    @SuppressWarnings("unchecked")
+    protected B self() {
+      return (B) this;
+    }
+
+    public B namespace(String namespace) {
       this.namespace = namespace;
-      return this;
+      return self();
     }
 
-    public Builder kind(String kind) {
+    public B kind(String kind) {
       this.kind = kind;
-      return this;
+      return self();
     }
 
-    public Builder startCursor(Cursor startCursor) {
+    public B startCursor(Cursor startCursor) {
       this.startCursor = startCursor;
-      return this;
+      return self();
     }
 
-    public Builder encCursor(Cursor endCursor) {
+    public B encCursor(Cursor endCursor) {
       this.endCursor = endCursor;
-      return this;
+      return self();
     }
 
-    public Builder offset(Integer offset) {
+    public B offset(Integer offset) {
       Preconditions.checkArgument(offset == null || offset >= 0, "offset must not be negative");
       this.offset = offset;
-      return this;
+      return self();
     }
 
-    public Builder limit(Integer limit) {
+    public B limit(Integer limit) {
       Preconditions.checkArgument(limit == null || offset > 0, "limit must be positive");
       this.limit = limit;
-      return this;
+      return self();
     }
 
-    /*
-    public StructuredQuery full() {
-      return new StructuredQuery<>(ResultType.full(), this, false);
+    public B filter(Filter filter) {
+      this.filter = filter;
+      return self();
     }
 
-    public StructuredQuery projection(String projection, String... other) {
-      ImmutableSet projections =
-          ImmutableSet.builder().add(projection).add(other).build();
-      return new StructuredQuery<>(ResultType.projection(), this, projections);
+    public B clearOrderBy() {
+      orderBy.clear();
+      return self();
     }
 
-    public StructuredQuery keyOnly() {
-      return new StructuredQuery<>(ResultType.keyOnly(), this, true);
+    public B addOrderBy(OrderBy orderBy, OrderBy... others) {
+      this.orderBy.add(orderBy);
+      for (OrderBy other : others) {
+        this.orderBy.add(other);
+      }
+      return self();
     }
-    */
   }
 
-
-
   private StructuredQuery(ResultType resultType, boolean keysOnly, String namespace, String kind,
       Cursor startCursor, Cursor endCursor, Integer offset, Integer limit,
-      ImmutableList projection, Filter filter, ImmutableList groupBy,
-      ImmutableList orderBy) {
+      Filter filter, ImmutableList orderBy) {
     super(resultType, namespace);
     this.keysOnly = keysOnly;
     this.kind = kind;
@@ -290,17 +445,14 @@ private StructuredQuery(ResultType resultType, boolean keysOnly, String names
     this.endCursor = endCursor;
     this.offset = offset;
     this.limit = limit;
-    this.projection = projection;
     this.filter = filter;
-    this.groupBy = groupBy;
     this.orderBy = orderBy;
-    Preconditions.checkState(keysOnly == false || projection.isEmpty() && groupBy.isEmpty(),
-        "projection or group by are not applicable for keys-only queries");
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(namespace(), keysOnly, kind, startCursor, endCursor, offset, limit, projection, filter, groupBy, orderBy);
+    return Objects.hash(
+        namespace(), keysOnly, kind, startCursor, endCursor, offset, limit, filter, orderBy);
   }
 
   @Override
@@ -318,19 +470,18 @@ public boolean equals(Object obj) {
         && Objects.equals(endCursor, other.endCursor)
         && Objects.equals(offset, other.offset)
         && Objects.equals(limit, other.limit)
-        && Objects.equals(projection, other.projection)
         && Objects.equals(filter, other.filter)
-        && Objects.equals(groupBy, other.groupBy)
         && Objects.equals(orderBy, other.orderBy);
   }
 
   @Override
-  protected void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb, int totalRead,
-      ByteString batchCursor) {
-    if (batchCursor == null) {
-      requestPb.setQuery(toPb());
-      return;
-    }
+  protected void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb) {
+    requestPb.setQuery(toPb());
+  }
+
+  @Override
+  protected GqlQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) {
+    // TODO: implment
     throw new UnsupportedOperationException("paging not implemented yet");
   }
 
@@ -358,6 +509,12 @@ protected DatastoreV1.Query toPb() {
     if (limit != null) {
       queryPb.setLimit(limit);
     }
+    if (filter != null) {
+      DatastoreV1.Filter.Builder filterPb = queryPb.getFilterBuilder();
+      if (filter.next == null) {
+        // TODO
+      }
+    }
     // TODO: projection, filter, groupBy, orderBy (or keys-only)
     return queryPb.build();
   }
diff --git a/src/main/java/com/google/gcloud/datastore/Validator.java b/src/main/java/com/google/gcloud/datastore/Validator.java
new file mode 100644
index 000000000000..2f915ccc3d93
--- /dev/null
+++ b/src/main/java/com/google/gcloud/datastore/Validator.java
@@ -0,0 +1,44 @@
+package com.google.gcloud.datastore;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Strings;
+
+import java.util.regex.Pattern;
+
+/**
+ * Utility to validate Datastore type/values.
+ */
+class Validator {
+
+  private static final Pattern DATASET_PATTERN = Pattern.compile(
+      "([a-z\\d\\-]{1,100}~)?([a-z\\d][a-z\\d\\-\\.]{0,99}\\:)?([a-z\\d][a-z\\d\\-]{0,99})");
+  private static final int MAX_NAMESPACE_LENGTH = 100;
+  private static final Pattern NAMESPACE_PATTERN =
+      Pattern.compile(String.format("[0-9A-Za-z\\._\\-]{0,%d}", MAX_NAMESPACE_LENGTH));
+
+
+  static String validateDataset(String dataset) {
+    checkArgument(!Strings.isNullOrEmpty(dataset), "dataset can't be empty or null");
+    checkArgument(Validator.DATASET_PATTERN.matcher(dataset).matches(),
+          "dataset must match the following pattern: " + Validator.DATASET_PATTERN.pattern());
+    return dataset;
+  }
+
+  static String validateNamespace(String namespace) {
+    if (namespace != null) {
+      checkArgument(!namespace.isEmpty(), "namespace must not be an empty string");
+      checkArgument(namespace.length() <= 100,
+          "namespace must not contain more than 100 characters");
+      checkArgument(Validator.NAMESPACE_PATTERN.matcher(namespace).matches(),
+          "namespace must the following pattern: " + Validator.NAMESPACE_PATTERN.pattern());
+    }
+    return namespace;
+  }
+
+  static String validateKind(String kind) {
+    checkArgument(!Strings.isNullOrEmpty(kind), "kind must not be empty or null");
+    checkArgument(kind.length() <= 500, "kind must not contain more than 500 characters");
+    return kind;
+  }
+}
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
index a1cd276e4be7..60525f39f894 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
@@ -73,7 +73,7 @@ public void setUp() {
 
   @Test
   public void testGetOptions() {
-    assertSame(options, datastore.getOptions());
+    assertSame(options, datastore.options());
   }
 
   @Test

From 63bafd931fd828b8a11d903da8681c4844679394 Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Fri, 12 Dec 2014 23:57:49 -0800
Subject: [PATCH 053/771] change filter composition

---
 .classpath                                    |  10 -
 .../gcloud/datastore/StructuredQuery.java     | 294 +++++++++---------
 2 files changed, 147 insertions(+), 157 deletions(-)

diff --git a/.classpath b/.classpath
index d6cf6121af66..9ed6ee4d0713 100644
--- a/.classpath
+++ b/.classpath
@@ -23,15 +23,5 @@
 		
 	
 	
-	
-		
-			
-		
-	
-	
-		
-			
-		
-	
 	
 
diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index 8c172b717051..684d31a8fcf4 100644
--- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -12,18 +12,13 @@
 import com.google.api.client.util.Preconditions;
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import java.io.Serializable;
-import java.util.ArrayDeque;
 import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
-import java.util.Set;
 
 public class StructuredQuery extends Query {
 
@@ -39,40 +34,60 @@ public class StructuredQuery extends Query {
   private final transient Integer limit;
 
 
-  public static class Expression implements Serializable {
+  public abstract static class Filter implements Serializable {
 
     private static final long serialVersionUID = -6443285436239990860L;
 
-    private final Operator operator = null;
-    private final List values = null;
+    Filter() {
+    }
+
+    protected abstract DatastoreV1.Filter toPb();
+  }
+
+
+  public static final class Expression extends Filter {
+
+    private static final long serialVersionUID = 3610352685739360009L;
+    private final Operator operator;
+    private final ImmutableList filters;
 
     enum Operator {
-      AND,
-      OR
+      AND;
+
+      DatastoreV1.CompositeFilter.Operator toPb() {
+        return DatastoreV1.CompositeFilter.Operator.valueOf(name());
+      }
     }
 
-    /*
-    Expression(Operator operator, Expression first, Expression... other) {
+    private Expression(Operator operator, Filter first, Filter... other) {
       this.operator = operator;
-      ImmutableList.builder().
-      this.
+      this.filters =
+          ImmutableList.builder().add(first).addAll(Arrays.asList(other)).build();
     }
-    */
-  }
 
-/*
-  public static final class And extends Expression {
+    public static Expression and(Filter first, Filter... other) {
+      return new Expression(Operator.AND, first, other);
+    }
 
+    @Override
+    protected DatastoreV1.Filter toPb() {
+      DatastoreV1.Filter.Builder filterPb = DatastoreV1.Filter.newBuilder();
+      DatastoreV1.CompositeFilter.Builder compositeFilterPb = filterPb.getCompositeFilterBuilder();
+      compositeFilterPb.setOperator(operator.toPb());
+      for (Filter filter : filters) {
+        compositeFilterPb.addFilter(filter.toPb());
+      }
+      return filterPb.build();
+    }
   }
-*/
-  public static final class Filter extends Expression {
+
+  public static final class Condition extends Filter {
 
     private static final long serialVersionUID = -4514695915258598597L;
 
     private final String property;
     private final Operator operator;
     private final Value value;
-    private final Filter next;
 
     enum Operator {
       LESS_THAN,
@@ -81,34 +96,33 @@ enum Operator {
       GREATER_THAN_OR_EQUAL,
       EQUAL,
       HAS_ANCESTOR,
-      IS_NULL,
-      AND
+      IS_NULL {
+        @Override
+        public DatastoreV1.PropertyFilter.Operator toPb() {
+          return DatastoreV1.PropertyFilter.Operator.valueOf(EQUAL.name());
+        }
+      };
+
+      public DatastoreV1.PropertyFilter.Operator toPb() {
+        return DatastoreV1.PropertyFilter.Operator.valueOf(name());
+      }
     }
 
-    private Filter(String property, Operator operator, Value value) {
+    private Condition(String property, Operator operator, Value value) {
       this.property = checkNotNull(property);
       this.operator = checkNotNull(operator);
       this.value = checkNotNull(value);
-      this.next = null;
     }
 
-    private Filter(String property, Operator operator) {
+    private Condition(String property, Operator operator) {
       this.property = checkNotNull(property);
       this.operator = checkNotNull(operator);
       this.value = null;
-      this.next = null;
-    }
-
-    private Filter(Filter from, Filter next) {
-      this.property = from.property;
-      this.operator = from.operator;
-      this.value = from.value;
-      this.next = next;
     }
 
     @Override
     public int hashCode() {
-      return Objects.hash(property, operator, value, next);
+      return Objects.hash(property, operator, value);
     }
 
     @Override
@@ -116,204 +130,193 @@ public boolean equals(Object obj) {
       if (obj == this) {
         return true;
       }
-      if (!(obj instanceof Filter)) {
+      if (!(obj instanceof Condition)) {
         return false;
       }
-      Filter other = (Filter) obj;
+      Condition other = (Condition) obj;
       return property.equals(other.property)
           && operator.equals(other.operator)
-          && Objects.equals(value, other.value)
-          && Objects.equals(next, other.next);
+          && Objects.equals(value, other.value);
     }
 
-    public static Filter le(String property, Value value) {
-      return new Filter(property, Operator.LESS_THAN, value);
+    public static Condition le(String property, Value value) {
+      return new Condition(property, Operator.LESS_THAN, value);
     }
 
-    public static Filter le(String property, String value) {
-      return new Filter(property, Operator.LESS_THAN, of(value));
+    public static Condition le(String property, String value) {
+      return new Condition(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Filter le(String property, long value) {
-      return new Filter(property, Operator.LESS_THAN, of(value));
+    public static Condition le(String property, long value) {
+      return new Condition(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Filter le(String property, double value) {
-      return new Filter(property, Operator.LESS_THAN, of(value));
+    public static Condition le(String property, double value) {
+      return new Condition(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Filter le(String property, boolean value) {
-      return new Filter(property, Operator.LESS_THAN, of(value));
+    public static Condition le(String property, boolean value) {
+      return new Condition(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Filter le(String property, DateTime value) {
-      return new Filter(property, Operator.LESS_THAN, of(value));
+    public static Condition le(String property, DateTime value) {
+      return new Condition(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Filter le(String property, Key value) {
-      return new Filter(property, Operator.LESS_THAN, of(value));
+    public static Condition le(String property, Key value) {
+      return new Condition(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Filter le(String property, Blob value) {
-      return new Filter(property, Operator.LESS_THAN, of(value));
+    public static Condition le(String property, Blob value) {
+      return new Condition(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Filter lte(String property, Value value) {
-      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, value);
+    public static Condition lte(String property, Value value) {
+      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, value);
     }
 
-    public static Filter lte(String property, String value) {
-      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static Condition lte(String property, String value) {
+      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter lte(String property, long value) {
-      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static Condition lte(String property, long value) {
+      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter lte(String property, double value) {
-      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static Condition lte(String property, double value) {
+      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter lte(String property, boolean value) {
-      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static Condition lte(String property, boolean value) {
+      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter lte(String property, DateTime value) {
-      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static Condition lte(String property, DateTime value) {
+      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter lte(String property, Key value) {
-      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static Condition lte(String property, Key value) {
+      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter lte(String property, Blob value) {
-      return new Filter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static Condition lte(String property, Blob value) {
+      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter gt(String property, Value value) {
-      return new Filter(property, Operator.GREATER_THAN, value);
+    public static Condition gt(String property, Value value) {
+      return new Condition(property, Operator.GREATER_THAN, value);
     }
 
-    public static Filter gt(String property, String value) {
-      return new Filter(property, Operator.GREATER_THAN, of(value));
+    public static Condition gt(String property, String value) {
+      return new Condition(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Filter gt(String property, long value) {
-      return new Filter(property, Operator.GREATER_THAN, of(value));
+    public static Condition gt(String property, long value) {
+      return new Condition(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Filter gt(String property, double value) {
-      return new Filter(property, Operator.GREATER_THAN, of(value));
+    public static Condition gt(String property, double value) {
+      return new Condition(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Filter gt(String property, boolean value) {
-      return new Filter(property, Operator.GREATER_THAN, of(value));
+    public static Condition gt(String property, boolean value) {
+      return new Condition(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Filter gt(String property, DateTime value) {
-      return new Filter(property, Operator.GREATER_THAN, of(value));
+    public static Condition gt(String property, DateTime value) {
+      return new Condition(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Filter gt(String property, Key value) {
-      return new Filter(property, Operator.GREATER_THAN, of(value));
+    public static Condition gt(String property, Key value) {
+      return new Condition(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Filter gt(String property, Blob value) {
-      return new Filter(property, Operator.GREATER_THAN, of(value));
+    public static Condition gt(String property, Blob value) {
+      return new Condition(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Filter gte(String property, Value value) {
-      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, value);
+    public static Condition gte(String property, Value value) {
+      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, value);
     }
 
-    public static Filter gte(String property, String value) {
-      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static Condition gte(String property, String value) {
+      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter gte(String property, long value) {
-      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static Condition gte(String property, long value) {
+      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter gte(String property, double value) {
-      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static Condition gte(String property, double value) {
+      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter gte(String property, boolean value) {
-      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static Condition gte(String property, boolean value) {
+      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter gte(String property, DateTime value) {
-      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static Condition gte(String property, DateTime value) {
+      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter gte(String property, Key value) {
-      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static Condition gte(String property, Key value) {
+      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter gte(String property, Blob value) {
-      return new Filter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static Condition gte(String property, Blob value) {
+      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Filter eq(String property, Value value) {
-      return new Filter(property, Operator.EQUAL, value);
+    public static Condition eq(String property, Value value) {
+      return new Condition(property, Operator.EQUAL, value);
     }
 
-    public static Filter eq(String property, String value) {
-      return new Filter(property, Operator.EQUAL, of(value));
+    public static Condition eq(String property, String value) {
+      return new Condition(property, Operator.EQUAL, of(value));
     }
 
-    public static Filter eq(String property, long value) {
-      return new Filter(property, Operator.EQUAL, of(value));
+    public static Condition eq(String property, long value) {
+      return new Condition(property, Operator.EQUAL, of(value));
     }
 
-    public static Filter eq(String property, double value) {
-      return new Filter(property, Operator.EQUAL, of(value));
+    public static Condition eq(String property, double value) {
+      return new Condition(property, Operator.EQUAL, of(value));
     }
 
-    public static Filter eq(String property, boolean value) {
-      return new Filter(property, Operator.EQUAL, of(value));
+    public static Condition eq(String property, boolean value) {
+      return new Condition(property, Operator.EQUAL, of(value));
     }
 
-    public static Filter eq(String property, DateTime value) {
-      return new Filter(property, Operator.EQUAL, of(value));
+    public static Condition eq(String property, DateTime value) {
+      return new Condition(property, Operator.EQUAL, of(value));
     }
 
-    public static Filter eq(String property, Key value) {
-      return new Filter(property, Operator.EQUAL, of(value));
+    public static Condition eq(String property, Key value) {
+      return new Condition(property, Operator.EQUAL, of(value));
     }
 
-    public static Filter eq(String property, Blob value) {
-      return new Filter(property, Operator.EQUAL, of(value));
+    public static Condition eq(String property, Blob value) {
+      return new Condition(property, Operator.EQUAL, of(value));
     }
 
-    public static Filter hasAncestor(String property) {
-      return new Filter(property, Operator.HAS_ANCESTOR);
+    public static Condition hasAncestor(String property) {
+      return new Condition(property, Operator.HAS_ANCESTOR);
     }
 
-    public static Filter isNull(String property) {
-      return new Filter(property, Operator.IS_NULL);
+    public static Condition isNull(String property) {
+      return new Condition(property, Operator.IS_NULL, NullValue.of());
     }
 
-    public static Filter and(Filter first, Filter... other) {
-      Set combined = new LinkedHashSet<>();
-      for (Filter f : Iterables.concat(Collections.singleton(first), Arrays.asList(other))) {
-        while (f != null) {
-          combined.add(new Filter(f, null));
-          f = f.next;
-        }
-      }
-      ArrayDeque stack = new ArrayDeque<>(combined.size());
-      for (Filter f : combined) {
-        stack.push(f);
-      }
-      while (true) {
-        Filter top = stack.pop();
-        if (stack.isEmpty()) {
-          return top;
-        }
-        Filter beforeTop = stack.pop();
-        stack.push(new Filter(beforeTop, top));
+    @Override
+    protected DatastoreV1.Filter toPb() {
+      DatastoreV1.Filter.Builder filterPb = DatastoreV1.Filter.newBuilder();
+      DatastoreV1.PropertyFilter.Builder propertyFilterPb = filterPb.getPropertyFilterBuilder();
+      propertyFilterPb.getPropertyBuilder().setName(property);
+      propertyFilterPb.setOperator(operator.toPb());
+      if (value != null) {
+        propertyFilterPb.setValue(value.toPb());
       }
+      return filterPb.build();
     }
   }
 
@@ -510,12 +513,9 @@ protected DatastoreV1.Query toPb() {
       queryPb.setLimit(limit);
     }
     if (filter != null) {
-      DatastoreV1.Filter.Builder filterPb = queryPb.getFilterBuilder();
-      if (filter.next == null) {
-        // TODO
-      }
+      queryPb.setFilter(filter.toPb());
     }
-    // TODO: projection, filter, groupBy, orderBy (or keys-only)
+    // TODO: projection, groupBy, orderBy (or keys-only)
     return queryPb.build();
   }
 

From a18313fbf6c8816c8e0cfadcfd94a56942449f35 Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Sun, 14 Dec 2014 11:53:21 -0800
Subject: [PATCH 054/771] structured query - work in progress

---
 .../com/google/gcloud/datastore/GqlQuery.java |  18 +-
 .../com/google/gcloud/datastore/Query.java    | 130 +++--
 .../google/gcloud/datastore/QueryResult.java  |  43 +-
 .../gcloud/datastore/QueryResultImpl.java     |  18 +-
 .../gcloud/datastore/StructuredQuery.java     | 448 +++++++++++++-----
 .../gcloud/datastore/SerializationTest.java   |  11 +-
 6 files changed, 462 insertions(+), 206 deletions(-)

diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
index a1ffa9f6ff70..d6827e7d523e 100644
--- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
@@ -116,15 +116,15 @@ static Argument fromPb(DatastoreV1.GqlQueryArg argPb) {
    */
   public static final class Builder {
 
-    private final ResultType resultType;
+    private final ResultClass resultClass;
     private String namespace;
     private String queryString;
     private boolean allowLiteral;
     private Map nameArgs = new TreeMap<>();
     private List numberArgs = new LinkedList<>();
 
-    Builder(ResultType resultType, String query) {
-      this.resultType = resultType;
+    Builder(ResultClass resultClass, String query) {
+      this.resultClass = checkNotNull(resultClass);
       queryString = checkNotNull(query);
     }
 
@@ -268,7 +268,7 @@ private static Argument toArgument(String name, Value.BuilderFactory builderFact
   }
 
   private GqlQuery(Builder builder) {
-    super(builder.resultType, builder.namespace);
+    super(builder.resultClass, builder.namespace);
     queryString = builder.queryString;
     allowLiteral = builder.allowLiteral;
     nameArgs = ImmutableList.copyOf(builder.nameArgs.values());
@@ -367,12 +367,12 @@ protected GqlQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) {
   }
 
   @Override
-  protected Object fromPb(ResultType resultType, String namespace, byte[] bytesPb)
+  protected Object fromPb(ResultClass resultType, String namespace, byte[] bytesPb)
       throws InvalidProtocolBufferException {
     return fromPb(resultType, namespace, DatastoreV1.GqlQuery.parseFrom(bytesPb));
   }
 
-  static  GqlQuery fromPb(ResultType resultType, String namespace,
+  static  GqlQuery fromPb(ResultClass resultType, String namespace,
       DatastoreV1.GqlQuery queryPb) {
     Builder builder = new Builder<>(resultType, queryPb.getQueryString());
     builder.namespace(namespace);
@@ -396,7 +396,7 @@ static  GqlQuery fromPb(ResultType resultType, String namespace,
    * @see GQL Reference
    */
   public static GqlQuery.Builder builder(String gql) {
-    return builder(ResultType.unknown(), gql);
+    return builder(ResultClass.unknown(), gql);
   }
 
   /**
@@ -404,7 +404,7 @@ public static GqlQuery.Builder builder(String gql) {
    *
    * @see GQL Reference
    */
-  public static  GqlQuery.Builder builder(ResultType resultType, String gql) {
-    return new GqlQuery.Builder<>(resultType, gql);
+  public static  GqlQuery.Builder builder(ResultClass resultClass, String gql) {
+    return new GqlQuery.Builder<>(resultClass, gql);
   }
 }
diff --git a/src/main/java/com/google/gcloud/datastore/Query.java b/src/main/java/com/google/gcloud/datastore/Query.java
index bf474bc73d1e..4959289e67ec 100644
--- a/src/main/java/com/google/gcloud/datastore/Query.java
+++ b/src/main/java/com/google/gcloud/datastore/Query.java
@@ -1,11 +1,10 @@
 package com.google.gcloud.datastore;
 
-import static com.google.api.client.util.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.gcloud.datastore.QueryResult.Type;
 import com.google.protobuf.GeneratedMessage;
 import com.google.protobuf.InvalidProtocolBufferException;
 
@@ -21,59 +20,134 @@ public abstract class Query extends Serializable {
 
   private static final long serialVersionUID = -2748141759901313101L;
 
-  private final ResultType resultType;
+  private final ResultClass resultClass;
   private final String namespace;
 
-  public static class ResultType implements java.io.Serializable {
+  public static class ResultClass implements java.io.Serializable {
 
     private static final long serialVersionUID = 2104157695425806623L;
-    private static final ResultType UNKNOWN = new ResultType<>(null, null);
-    private static final ResultType FULL = new ResultType<>(Entity.class, Type.FULL);
-    private static final ResultType KEY_ONLY = new ResultType<>(Key.class, Type.KEY_ONLY);
-    private static final ResultType PROJECTION =
-        new ResultType<>(PartialEntity.class, Type.PROJECTION);
+    private static final ResultClass UNKNOWN = new ResultClass<>(Object.class);
+    private static final ResultClass FULL = new ResultClass<>(Entity.class);
+    private static final ResultClass KEY_ONLY = new ResultClass<>(Key.class);
+    private static final ResultClass PROJECTION =
+        new ResultClass<>(PartialEntity.class);
 
+    private final Class value;
 
-    private final Class clazz;
-    private final Type type;
+    private ResultClass(Class value) {
+      this.value = checkNotNull(value);
+    }
+
+    public Class value() {
+      return value;
+    }
 
-    private ResultType(Class clazz, Type type) {
-      this.clazz = clazz;
-      this.type = type;
+    @Override
+    public int hashCode() {
+      return value.hashCode();
     }
 
-    public Type getType() {
-      return type;
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == this) {
+        return true;
+      }
+      if (!(obj instanceof ResultClass)) {
+        return false;
+      }
+      ResultClass other = (ResultClass) obj;
+      return value.equals(other.value);
     }
 
-    public Class getResultClass() {
-      return clazz;
+    @Override
+    public String toString() {
+      ToStringHelper toStringHelper = MoreObjects.toStringHelper(this);
+      toStringHelper.add("value", value);
+      return toStringHelper.toString();
     }
 
-    public static ResultType unknown() {
+    boolean isAssignableFrom(ResultClass resultClass) {
+      return value.isAssignableFrom(resultClass.value);
+    }
+
+    static ResultClass unknown() {
       return UNKNOWN;
     }
 
-    public static ResultType full() {
+    public static ResultClass full() {
       return FULL;
     }
 
-    public static ResultType projection() {
+    public static ResultClass projection() {
       return PROJECTION;
     }
 
-    public static ResultType keyOnly() {
+    public static ResultClass keyOnly() {
       return KEY_ONLY;
     }
   }
 
-  Query(ResultType resultType, String namespace) {
-    this.resultType = checkNotNull(resultType);
+  /**
+   * Possible results types are:
+   *   FULL: A complete {@link Entity}.
+   *   PROJECTION: A partial entity, represented by {@link PartialEntity}.
+   *   KEY_ONLY: An entity's {@link Key}.
+   */
+  public static enum Type {
+
+    FULL {
+      @Override
+      @SuppressWarnings("unchecked")
+      Entity convert(DatastoreV1.Entity value) {
+        return Entity.fromPb(value);
+      }
+
+      @Override
+      ResultClass resultClass() {
+        return ResultClass.full();
+      }
+    },
+
+    PROJECTION  {
+
+      @Override
+      @SuppressWarnings("unchecked")
+      PartialEntity convert(DatastoreV1.Entity value) {
+        return PartialEntity.fromPb(value);
+      }
+
+      @Override
+      ResultClass resultClass() {
+        return ResultClass.projection();
+      }
+    },
+
+    KEY_ONLY {
+
+      @Override
+      @SuppressWarnings("unchecked")
+      Key convert(DatastoreV1.Entity value) {
+        return Key.fromPb(value.getKey());
+      }
+
+      @Override
+      ResultClass resultClass() {
+        return ResultClass.projection();
+      }
+    };
+
+    abstract  T convert(DatastoreV1.Entity value);
+
+    abstract ResultClass resultClass();
+  }
+
+  Query(ResultClass resultClass, String namespace) {
+    this.resultClass = checkNotNull(resultClass);
     this.namespace = namespace;
   }
 
-  public ResultType resultType() {
-    return resultType;
+  ResultClass getResultClass() {
+    return resultClass;
   }
 
   public String namespace() {
@@ -90,10 +164,10 @@ public String toString() {
 
   @Override
   protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException {
-    return fromPb(resultType, namespace, bytesPb);
+    return fromPb(resultClass, namespace, bytesPb);
   }
 
-  protected abstract Object fromPb(ResultType resultType, String namespace, byte[] bytesPb)
+  protected abstract Object fromPb(ResultClass resultClass, String namespace, byte[] bytesPb)
       throws InvalidProtocolBufferException;
 
   protected abstract void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb);
diff --git a/src/main/java/com/google/gcloud/datastore/QueryResult.java b/src/main/java/com/google/gcloud/datastore/QueryResult.java
index 9045764077e4..7f46203ff3f0 100644
--- a/src/main/java/com/google/gcloud/datastore/QueryResult.java
+++ b/src/main/java/com/google/gcloud/datastore/QueryResult.java
@@ -1,55 +1,16 @@
 package com.google.gcloud.datastore;
 
-import com.google.api.services.datastore.DatastoreV1;
-
 import java.util.Iterator;
 
 /**
  * The result of a Google Cloud Datastore query submission.
  * When the result is not typed it is possible to cast it to its appropriate type according to
- * the {@link #getType} value.
+ * the {@link #getType} result.
  *
  * @param V the type of values the result holds.
  */
 public interface QueryResult extends Iterator {
 
-  /**
-   * Possible results types are:
-   *   FULL: A complete {@link Entity}.
-   *   PROJECTION: A partial entity, represented by {@link PartialEntity}.
-   *   KEY_ONLY: An entity's {@link Key}.
-   */
-  enum Type {
-
-    FULL {
-      @Override
-      @SuppressWarnings("unchecked")
-      Entity convert(DatastoreV1.Entity value) {
-        return Entity.fromPb(value);
-      }
-    },
-
-    PROJECTION  {
-
-      @Override
-      @SuppressWarnings("unchecked")
-      PartialEntity convert(DatastoreV1.Entity value) {
-        return PartialEntity.fromPb(value);
-      }
-    },
-
-    KEY_ONLY {
-
-      @Override
-      @SuppressWarnings("unchecked")
-      Key convert(DatastoreV1.Entity value) {
-        return Key.fromPb(value.getKey());
-      }
-    };
-
-    abstract  T convert(DatastoreV1.Entity value);
-  }
-
   /**
    * Returns the actual type of the result's values.
    * When needed the result could be casted accordingly:
@@ -59,7 +20,7 @@ Key convert(DatastoreV1.Entity value) {
    * Type.KEY_ONLY -> (QueryResult)
    * } 
    */
-  Type getType();
+  Query.Type getType();
 
   /**
    * Return the Cursor for the next result. Not currently implemented (depends on v1beta3).
diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
index 734b8b1bee4a..7e50d0bef5d6 100644
--- a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
@@ -9,23 +9,24 @@
 
 class QueryResultImpl implements QueryResult {
 
-  private static final ImmutableMap
+  private static final ImmutableMap
       RESULT_TYPE_CONVERTER;
 
   private final DatastoreServiceImpl datastore;
   private final DatastoreV1.ReadOptions readOptionsPb;
-  private final QueryResult.Type type;
+  private final Query.ResultClass resultClass;
   private Query query;
+  private Query.Type type;
   private DatastoreV1.QueryResultBatch resultPb;
   private Iterator entityResultPbIter;
   private ByteString endCursor;
   private int count;
 
   static {
-    ImmutableMap.Builder builder =
+    ImmutableMap.Builder builder =
         ImmutableMap.builder();
     for (DatastoreV1.EntityResult.ResultType type : DatastoreV1.EntityResult.ResultType.values()) {
-      builder.put(type, QueryResult.Type.valueOf(type.name()));
+      builder.put(type, Query.Type.valueOf(type.name()));
     }
     RESULT_TYPE_CONVERTER = builder.build();
   }
@@ -35,10 +36,8 @@ class QueryResultImpl implements QueryResult {
     this.datastore = datastore;
     this.readOptionsPb = readOptionsPb;
     this.query = query;
+    this.resultClass = query.getResultClass();
     sendRequest();
-    type = RESULT_TYPE_CONVERTER.get(resultPb.getEntityResultType());
-    Preconditions.checkState(query.resultType().getType() == null
-        || query.resultType().getType() == type, "Unexpected result type");
   }
 
   private DatastoreV1.QueryResultBatch sendRequest() {
@@ -63,6 +62,9 @@ private DatastoreV1.QueryResultBatch sendRequest() {
     } else {
       endCursor = null;
     }
+    type = RESULT_TYPE_CONVERTER.get(resultPb.getEntityResultType());
+    Preconditions.checkState(resultClass.isAssignableFrom(type.resultClass()),
+        "Unexpected result type");
     return resultPb;
   }
 
@@ -88,7 +90,7 @@ public void remove() {
   }
 
   @Override
-  public QueryResult.Type getType() {
+  public Query.Type getType() {
     return type;
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index 684d31a8fcf4..ac5f8519cf5c 100644
--- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -20,13 +20,14 @@
 import java.util.List;
 import java.util.Objects;
 
-public class StructuredQuery extends Query {
+public abstract class StructuredQuery extends Query {
 
   private static final long serialVersionUID = 546838955624019594L;
 
-  private final transient boolean keysOnly;
   private final transient String kind;
+  private final ImmutableList projection;
   private final transient Filter filter;
+  private final ImmutableList groupBy;
   private final transient ImmutableList orderBy;
   private final transient Cursor startCursor;
   private final transient Cursor endCursor;
@@ -44,8 +45,7 @@ public abstract static class Filter implements Serializable {
     protected abstract DatastoreV1.Filter toPb();
   }
 
-
-  public static final class Expression extends Filter {
+  public static final class CompositeFilter extends Filter {
 
     private static final long serialVersionUID = 3610352685739360009L;
     private final Operator operator;
@@ -59,14 +59,14 @@ DatastoreV1.CompositeFilter.Operator toPb() {
       }
     }
 
-    private Expression(Operator operator, Filter first, Filter... other) {
+    private CompositeFilter(Operator operator, Filter first, Filter... other) {
       this.operator = operator;
       this.filters =
           ImmutableList.builder().add(first).addAll(Arrays.asList(other)).build();
     }
 
-    public static Expression and(Filter first, Filter... other) {
-      return new Expression(Operator.AND, first, other);
+    public static CompositeFilter and(Filter first, Filter... other) {
+      return new CompositeFilter(Operator.AND, first, other);
     }
 
     @Override
@@ -81,7 +81,7 @@ protected DatastoreV1.Filter toPb() {
     }
   }
 
-  public static final class Condition extends Filter {
+  public static final class PropertyFilter extends Filter {
 
     private static final long serialVersionUID = -4514695915258598597L;
 
@@ -108,13 +108,13 @@ public DatastoreV1.PropertyFilter.Operator toPb() {
       }
     }
 
-    private Condition(String property, Operator operator, Value value) {
+    private PropertyFilter(String property, Operator operator, Value value) {
       this.property = checkNotNull(property);
       this.operator = checkNotNull(operator);
       this.value = checkNotNull(value);
     }
 
-    private Condition(String property, Operator operator) {
+    private PropertyFilter(String property, Operator operator) {
       this.property = checkNotNull(property);
       this.operator = checkNotNull(operator);
       this.value = null;
@@ -130,181 +130,181 @@ public boolean equals(Object obj) {
       if (obj == this) {
         return true;
       }
-      if (!(obj instanceof Condition)) {
+      if (!(obj instanceof PropertyFilter)) {
         return false;
       }
-      Condition other = (Condition) obj;
+      PropertyFilter other = (PropertyFilter) obj;
       return property.equals(other.property)
           && operator.equals(other.operator)
           && Objects.equals(value, other.value);
     }
 
-    public static Condition le(String property, Value value) {
-      return new Condition(property, Operator.LESS_THAN, value);
+    public static PropertyFilter le(String property, Value value) {
+      return new PropertyFilter(property, Operator.LESS_THAN, value);
     }
 
-    public static Condition le(String property, String value) {
-      return new Condition(property, Operator.LESS_THAN, of(value));
+    public static PropertyFilter le(String property, String value) {
+      return new PropertyFilter(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Condition le(String property, long value) {
-      return new Condition(property, Operator.LESS_THAN, of(value));
+    public static PropertyFilter le(String property, long value) {
+      return new PropertyFilter(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Condition le(String property, double value) {
-      return new Condition(property, Operator.LESS_THAN, of(value));
+    public static PropertyFilter le(String property, double value) {
+      return new PropertyFilter(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Condition le(String property, boolean value) {
-      return new Condition(property, Operator.LESS_THAN, of(value));
+    public static PropertyFilter le(String property, boolean value) {
+      return new PropertyFilter(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Condition le(String property, DateTime value) {
-      return new Condition(property, Operator.LESS_THAN, of(value));
+    public static PropertyFilter le(String property, DateTime value) {
+      return new PropertyFilter(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Condition le(String property, Key value) {
-      return new Condition(property, Operator.LESS_THAN, of(value));
+    public static PropertyFilter le(String property, Key value) {
+      return new PropertyFilter(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Condition le(String property, Blob value) {
-      return new Condition(property, Operator.LESS_THAN, of(value));
+    public static PropertyFilter le(String property, Blob value) {
+      return new PropertyFilter(property, Operator.LESS_THAN, of(value));
     }
 
-    public static Condition lte(String property, Value value) {
-      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, value);
+    public static PropertyFilter lte(String property, Value value) {
+      return new PropertyFilter(property, Operator.LESS_THAN_OR_EQUAL, value);
     }
 
-    public static Condition lte(String property, String value) {
-      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter lte(String property, String value) {
+      return new PropertyFilter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition lte(String property, long value) {
-      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter lte(String property, long value) {
+      return new PropertyFilter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition lte(String property, double value) {
-      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter lte(String property, double value) {
+      return new PropertyFilter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition lte(String property, boolean value) {
-      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter lte(String property, boolean value) {
+      return new PropertyFilter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition lte(String property, DateTime value) {
-      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter lte(String property, DateTime value) {
+      return new PropertyFilter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition lte(String property, Key value) {
-      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter lte(String property, Key value) {
+      return new PropertyFilter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition lte(String property, Blob value) {
-      return new Condition(property, Operator.LESS_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter lte(String property, Blob value) {
+      return new PropertyFilter(property, Operator.LESS_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition gt(String property, Value value) {
-      return new Condition(property, Operator.GREATER_THAN, value);
+    public static PropertyFilter gt(String property, Value value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN, value);
     }
 
-    public static Condition gt(String property, String value) {
-      return new Condition(property, Operator.GREATER_THAN, of(value));
+    public static PropertyFilter gt(String property, String value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Condition gt(String property, long value) {
-      return new Condition(property, Operator.GREATER_THAN, of(value));
+    public static PropertyFilter gt(String property, long value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Condition gt(String property, double value) {
-      return new Condition(property, Operator.GREATER_THAN, of(value));
+    public static PropertyFilter gt(String property, double value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Condition gt(String property, boolean value) {
-      return new Condition(property, Operator.GREATER_THAN, of(value));
+    public static PropertyFilter gt(String property, boolean value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Condition gt(String property, DateTime value) {
-      return new Condition(property, Operator.GREATER_THAN, of(value));
+    public static PropertyFilter gt(String property, DateTime value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Condition gt(String property, Key value) {
-      return new Condition(property, Operator.GREATER_THAN, of(value));
+    public static PropertyFilter gt(String property, Key value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Condition gt(String property, Blob value) {
-      return new Condition(property, Operator.GREATER_THAN, of(value));
+    public static PropertyFilter gt(String property, Blob value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN, of(value));
     }
 
-    public static Condition gte(String property, Value value) {
-      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, value);
+    public static PropertyFilter gte(String property, Value value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN_OR_EQUAL, value);
     }
 
-    public static Condition gte(String property, String value) {
-      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter gte(String property, String value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition gte(String property, long value) {
-      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter gte(String property, long value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition gte(String property, double value) {
-      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter gte(String property, double value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition gte(String property, boolean value) {
-      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter gte(String property, boolean value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition gte(String property, DateTime value) {
-      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter gte(String property, DateTime value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition gte(String property, Key value) {
-      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter gte(String property, Key value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition gte(String property, Blob value) {
-      return new Condition(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
+    public static PropertyFilter gte(String property, Blob value) {
+      return new PropertyFilter(property, Operator.GREATER_THAN_OR_EQUAL, of(value));
     }
 
-    public static Condition eq(String property, Value value) {
-      return new Condition(property, Operator.EQUAL, value);
+    public static PropertyFilter eq(String property, Value value) {
+      return new PropertyFilter(property, Operator.EQUAL, value);
     }
 
-    public static Condition eq(String property, String value) {
-      return new Condition(property, Operator.EQUAL, of(value));
+    public static PropertyFilter eq(String property, String value) {
+      return new PropertyFilter(property, Operator.EQUAL, of(value));
     }
 
-    public static Condition eq(String property, long value) {
-      return new Condition(property, Operator.EQUAL, of(value));
+    public static PropertyFilter eq(String property, long value) {
+      return new PropertyFilter(property, Operator.EQUAL, of(value));
     }
 
-    public static Condition eq(String property, double value) {
-      return new Condition(property, Operator.EQUAL, of(value));
+    public static PropertyFilter eq(String property, double value) {
+      return new PropertyFilter(property, Operator.EQUAL, of(value));
     }
 
-    public static Condition eq(String property, boolean value) {
-      return new Condition(property, Operator.EQUAL, of(value));
+    public static PropertyFilter eq(String property, boolean value) {
+      return new PropertyFilter(property, Operator.EQUAL, of(value));
     }
 
-    public static Condition eq(String property, DateTime value) {
-      return new Condition(property, Operator.EQUAL, of(value));
+    public static PropertyFilter eq(String property, DateTime value) {
+      return new PropertyFilter(property, Operator.EQUAL, of(value));
     }
 
-    public static Condition eq(String property, Key value) {
-      return new Condition(property, Operator.EQUAL, of(value));
+    public static PropertyFilter eq(String property, Key value) {
+      return new PropertyFilter(property, Operator.EQUAL, of(value));
     }
 
-    public static Condition eq(String property, Blob value) {
-      return new Condition(property, Operator.EQUAL, of(value));
+    public static PropertyFilter eq(String property, Blob value) {
+      return new PropertyFilter(property, Operator.EQUAL, of(value));
     }
 
-    public static Condition hasAncestor(String property) {
-      return new Condition(property, Operator.HAS_ANCESTOR);
+    public static PropertyFilter hasAncestor(String property) {
+      return new PropertyFilter(property, Operator.HAS_ANCESTOR);
     }
 
-    public static Condition isNull(String property) {
-      return new Condition(property, Operator.IS_NULL, NullValue.of());
+    public static PropertyFilter isNull(String property) {
+      return new PropertyFilter(property, Operator.IS_NULL, NullValue.of());
     }
 
     @Override
@@ -371,16 +371,23 @@ public static OrderBy desc(String property) {
     }
   }
 
-  static class Builder> {
+  abstract static class BaseBuilder> {
 
+    private ResultClass resultClass;
     private String namespace;
     private String kind;
+    private List projection = new LinkedList<>();
+    private Filter filter;
+    private List groupBy = new LinkedList<>();
+    private List orderBy = new LinkedList<>();
     private Cursor startCursor;
     private Cursor endCursor;
     private Integer offset;
     private Integer limit;
-    private Filter filter;
-    private List orderBy = new LinkedList<>();
+
+    BaseBuilder(ResultClass resultClass) {
+      this.resultClass = resultClass;
+    }
 
     @SuppressWarnings("unchecked")
     protected B self() {
@@ -429,6 +436,12 @@ public B clearOrderBy() {
       return self();
     }
 
+    public B orderBy(OrderBy orderBy, OrderBy... others) {
+      clearOrderBy();
+      addOrderBy(orderBy, others);
+      return self();
+    }
+
     public B addOrderBy(OrderBy orderBy, OrderBy... others) {
       this.orderBy.add(orderBy);
       for (OrderBy other : others) {
@@ -436,26 +449,172 @@ public B addOrderBy(OrderBy orderBy, OrderBy... others) {
       }
       return self();
     }
+
+    protected B clearProjection() {
+      projection.clear();
+      return self();
+    }
+
+    protected B projection(String property, String... others) {
+      clearProjection();
+      addProjection(property, others);
+      return self();
+    }
+
+    protected B addProjection(String property, String... others) {
+      this.projection.add(property);
+      for (String other : others) {
+        this.projection.add(other);
+      }
+      return self();
+    }
+
+    protected B clearGroupBy() {
+      groupBy.clear();
+      return self();
+    }
+
+    protected B groupBy(String property, String... others) {
+      clearGroupBy();
+      addGroupBy(property, others);
+      return self();
+    }
+
+    protected B addGroupBy(String property, String... others) {
+      this.groupBy.add(property);
+      for (String other : others) {
+        this.groupBy.add(other);
+      }
+      return self();
+    }
+
+    public abstract StructuredQuery build();
+  }
+
+  public static final class FullQueryBuilder extends BaseBuilder {
+
+    FullQueryBuilder() {
+      super(ResultClass.full());
+    }
+
+    @Override
+    public FullQuery build() {
+      return new FullQuery(this);
+    }
+  }
+
+  public static final class KeyOnlyQueryBuilder extends BaseBuilder {
+
+    public KeyOnlyQueryBuilder() {
+      super(ResultClass.keyOnly());
+      projection("__key__");
+    }
+
+    @Override
+    public KeyOnlyQuery build() {
+      return new KeyOnlyQuery(this);
+    }
+  }
+
+  public static final class ProjectionQueryBuilder
+      extends BaseBuilder {
+
+    public ProjectionQueryBuilder() {
+      super(ResultClass.projection());
+    }
+
+    @Override
+    public ProjectionQuery build() {
+      return new ProjectionQuery(this);
+    }
+
+    @Override
+    public ProjectionQueryBuilder clearProjection() {
+      return super.clearProjection();
+    }
+
+    @Override
+    public ProjectionQueryBuilder projection(String property, String... others) {
+      return super.projection(property, others);
+    }
+
+    @Override
+    public ProjectionQueryBuilder addProjection(String property, String... others) {
+      return super.addProjection(property, others);
+    }
+
+    @Override
+    public ProjectionQueryBuilder clearGroupBy() {
+      return super.clearGroupBy();
+    }
+
+    @Override
+    public ProjectionQueryBuilder groupBy(String property, String... others) {
+      return super.groupBy(property, others);
+    }
+
+    @Override
+    public ProjectionQueryBuilder addGroupBy(String property, String... others) {
+      return super.addGroupBy(property, others);
+    }
+  }
+
+  public static final class FullQuery extends StructuredQuery {
+
+    private static final long serialVersionUID = -84461800292593840L;
+
+    FullQuery(FullQueryBuilder builder) {
+      super(builder);
+    }
+  }
+
+  public static final class KeyOnlyQuery extends StructuredQuery {
+
+    private static final long serialVersionUID = -7502917784216095473L;
+
+    KeyOnlyQuery(KeyOnlyQueryBuilder builder) {
+      super(builder);
+    }
   }
 
-  private StructuredQuery(ResultType resultType, boolean keysOnly, String namespace, String kind,
-      Cursor startCursor, Cursor endCursor, Integer offset, Integer limit,
-      Filter filter, ImmutableList orderBy) {
-    super(resultType, namespace);
-    this.keysOnly = keysOnly;
-    this.kind = kind;
-    this.startCursor = startCursor;
-    this.endCursor = endCursor;
-    this.offset = offset;
-    this.limit = limit;
-    this.filter = filter;
-    this.orderBy = orderBy;
+  public static final class ProjectionQuery extends StructuredQuery {
+
+    private static final long serialVersionUID = -3333183044486150649L;
+
+    ProjectionQuery(ProjectionQueryBuilder builder) {
+      super(builder);
+      Preconditions.checkState(!keyOnly(),
+          "Projection query can't project only '__key__', use KeyOnlyQuery instead.");
+    }
+
+    @Override
+    public List projection() {
+      return super.projection();
+    }
+
+    @Override
+    public List groupBy() {
+      return super.groupBy();
+    }
+  }
+
+  StructuredQuery(BaseBuilder builder) {
+    super(builder.resultClass, builder.namespace);
+    kind = builder.kind;
+    projection = ImmutableList.copyOf(builder.projection);
+    filter = builder.filter;
+    groupBy = ImmutableList.copyOf(builder.groupBy);
+    orderBy = ImmutableList.copyOf(builder.orderBy);
+    startCursor = builder.startCursor;
+    endCursor = builder.endCursor;
+    offset = builder.offset;
+    limit = builder.limit;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(
-        namespace(), keysOnly, kind, startCursor, endCursor, offset, limit, filter, orderBy);
+    return Objects.hash(namespace(), kind, startCursor, endCursor, offset, limit, filter, orderBy,
+        projection(), groupBy());
   }
 
   @Override
@@ -474,7 +633,50 @@ public boolean equals(Object obj) {
         && Objects.equals(offset, other.offset)
         && Objects.equals(limit, other.limit)
         && Objects.equals(filter, other.filter)
-        && Objects.equals(orderBy, other.orderBy);
+        && Objects.equals(orderBy, other.orderBy)
+        && Objects.equals(projection, other.projection)
+        && Objects.equals(groupBy, other.groupBy);
+
+  }
+
+  public String kind() {
+    return kind;
+  }
+
+  protected boolean keyOnly() {
+    return projection.size() == 1 && projection.get(0).equals("__key__");
+  }
+
+  protected List projection() {
+    return projection;
+  }
+
+  public Filter filter() {
+    return filter;
+  }
+
+  protected List groupBy() {
+    return groupBy;
+  }
+
+  public ImmutableList orderBy() {
+    return orderBy;
+  }
+
+  public Cursor etartCursor() {
+    return startCursor;
+  }
+
+  public Cursor endCursor() {
+    return endCursor;
+  }
+
+  public Integer offset() {
+    return offset;
+  }
+
+  public Integer limit() {
+    return limit;
   }
 
   @Override
@@ -489,9 +691,9 @@ protected GqlQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) {
   }
 
   @Override
-  protected Object fromPb(ResultType resultType, String namespace, byte[] bytesPb)
+  protected Object fromPb(ResultClass resultClass, String namespace, byte[] bytesPb)
       throws InvalidProtocolBufferException {
-    return fromPb(resultType, namespace, DatastoreV1.Query.parseFrom(bytesPb));
+    return fromPb(resultClass, namespace, DatastoreV1.Query.parseFrom(bytesPb));
   }
 
   @Override
@@ -519,9 +721,21 @@ protected DatastoreV1.Query toPb() {
     return queryPb.build();
   }
 
-  static  StructuredQuery fromPb(ResultType resultType, String namespace,
+  static  StructuredQuery fromPb(ResultClass resultType, String namespace,
       DatastoreV1.Query queryPb) {
     // TODO: implement
     return null;
   }
+
+  public static FullQueryBuilder builder() {
+    return new FullQueryBuilder();
+  }
+
+  public static KeyOnlyQueryBuilder keyOnlyBuilder() {
+    return new KeyOnlyQueryBuilder();
+  }
+
+  public static ProjectionQueryBuilder projectionBuilder() {
+    return new ProjectionQueryBuilder();
+  }
 }
diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
index ce3ea54f0a85..4c9f1fdad3c8 100644
--- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java
+++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
@@ -7,7 +7,7 @@
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.Multimap;
-import com.google.gcloud.datastore.Query.ResultType;
+import com.google.gcloud.datastore.Query.ResultClass;
 import com.google.gcloud.datastore.Value.Type;
 
 import org.junit.Test;
@@ -35,11 +35,16 @@ public class SerializationTest {
       .namespace("ns1")
       .build();
   private static final Query GQL2 =
-      GqlQuery.builder(ResultType.full(), "select * from kind1 where name = @name and age > @1")
+      GqlQuery.builder(ResultClass.full(), "select * from kind1 where name = @name and age > @1")
       .setArgument("name", "name1")
       .addArgument(20)
       .namespace("ns1")
       .build();
+  private static final Query QUERY1 = StructuredQuery.builder().kind("kind1").build();
+  private static final Query QUERY2 = StructuredQuery.keyOnlyBuilder().kind("k").filter(
+      StructuredQuery.PropertyFilter.eq("p1", "hello")).build();
+  private static final Query QUERY3 =
+      StructuredQuery.projectionBuilder().kind("k").projection("p").build();
   private static final KeyValue KEY_VALUE = KeyValue.of(KEY1);
   private static final NullValue NULL_VALUE = NullValue.builder().indexed(true).build();
   private static final StringValue STRING_VALUE = StringValue.of("hello");
@@ -108,7 +113,7 @@ public void testValues() throws Exception {
   public void testTypes() throws Exception {
     Object[] types = { KEY1, KEY2, INCOMPLETE_KEY1, INCOMPLETE_KEY2, ENTITY1, ENTITY2,
         ENTITY3, EMBEDDED_ENTITY1, EMBEDDED_ENTITY2, EMBEDDED_ENTITY3, DATE_TIME1,
-        BLOB1, CURSOR1, GQL1, GQL2};
+        BLOB1, CURSOR1, GQL1, GQL2, QUERY1, QUERY2, QUERY3};
     for (Object obj : types) {
       Object copy = serialiazeAndDeserialize(obj);
       assertEquals(obj, obj);

From 805d2ea724d4847bfe3f6726d4746573973a3b8e Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Sun, 14 Dec 2014 23:07:54 -0800
Subject: [PATCH 055/771] structured query work in progress

---
 .../google/gcloud/datastore/QueryResult.java  |  20 +-
 .../gcloud/datastore/QueryResultImpl.java     |  38 +--
 .../gcloud/datastore/StructuredQuery.java     | 272 +++++++++++++++---
 .../gcloud/datastore/SerializationTest.java   |   3 +-
 4 files changed, 248 insertions(+), 85 deletions(-)

diff --git a/src/main/java/com/google/gcloud/datastore/QueryResult.java b/src/main/java/com/google/gcloud/datastore/QueryResult.java
index 7f46203ff3f0..c569fc7bd361 100644
--- a/src/main/java/com/google/gcloud/datastore/QueryResult.java
+++ b/src/main/java/com/google/gcloud/datastore/QueryResult.java
@@ -4,26 +4,20 @@
 
 /**
  * The result of a Google Cloud Datastore query submission.
- * When the result is not typed it is possible to cast it to its appropriate type according to
- * the {@link #getType} result.
+ * When result is not typed it is possible to cast it to its appropriate type according to
+ * the {@link #resultClass} value.
  *
- * @param V the type of values the result holds.
+ * @param V the type of the results value.
  */
 public interface QueryResult extends Iterator {
 
   /**
-   * Returns the actual type of the result's values.
-   * When needed the result could be casted accordingly:
-   * 
 {@code
-   * Type.FULL -> (QueryResult)
-   * Type.PROJECTION -> (QueryResult)
-   * Type.KEY_ONLY -> (QueryResult)
-   * } 
+ * Returns the actual class of the result's values. */ - Query.Type getType(); + Class resultClass(); /** - * Return the Cursor for the next result. Not currently implemented (depends on v1beta3). + * Returns the Cursor for the next result. Not currently implemented (depends on v1beta3). */ - Cursor getCursor(); + Cursor cursor(); } diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java index 7e50d0bef5d6..3f81a7fd0010 100644 --- a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java +++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java @@ -2,12 +2,12 @@ import com.google.api.client.repackaged.com.google.common.base.Preconditions; import com.google.api.services.datastore.DatastoreV1; +import com.google.common.collect.AbstractIterator; import com.google.common.collect.ImmutableMap; -import com.google.protobuf.ByteString; import java.util.Iterator; -class QueryResultImpl implements QueryResult { +class QueryResultImpl extends AbstractIterator implements QueryResult { private static final ImmutableMap RESULT_TYPE_CONVERTER; @@ -19,8 +19,6 @@ class QueryResultImpl implements QueryResult { private Query.Type type; private DatastoreV1.QueryResultBatch resultPb; private Iterator entityResultPbIter; - private ByteString endCursor; - private int count; static { ImmutableMap.Builder builder = @@ -57,11 +55,6 @@ private DatastoreV1.QueryResultBatch sendRequest() { query.populatePb(requestPb); resultPb = datastore.runQuery(requestPb.build()).getBatch(); entityResultPbIter = resultPb.getEntityResultList().iterator(); - if (DatastoreV1.QueryResultBatch.MoreResultsType.NOT_FINISHED == resultPb.getMoreResults()) { - endCursor = resultPb.getEndCursor(); - } else { - endCursor = null; - } type = RESULT_TYPE_CONVERTER.get(resultPb.getEntityResultType()); Preconditions.checkState(resultClass.isAssignableFrom(type.resultClass()), "Unexpected result type"); @@ -69,33 +62,24 @@ private DatastoreV1.QueryResultBatch sendRequest() { } @Override - public boolean hasNext() { - return entityResultPbIter.hasNext() || endCursor != null; - } - - @Override - public T next() { - if (!hasNext() && endCursor != null) { + protected T computeNext() { + while (!entityResultPbIter.hasNext() + && resultPb.getMoreResults() == DatastoreV1.QueryResultBatch.MoreResultsType.NOT_FINISHED) { query = query.nextQuery(resultPb); sendRequest(); } - DatastoreV1.Entity entity = entityResultPbIter.next().getEntity(); - count++; - return type.convert(entity); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("QueryResult is read-only"); + return entityResultPbIter.hasNext() + ? type.convert(entityResultPbIter.next().getEntity()) + : endOfData(); } @Override - public Query.Type getType() { - return type; + public Class resultClass() { + return type.resultClass().value(); } @Override - public Cursor getCursor() { + public Cursor cursor() { // TODO(ozarov): implement when v1beta3 is available return null; } diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java index ac5f8519cf5c..a4d92290e964 100644 --- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java +++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java @@ -9,8 +9,10 @@ import static com.google.gcloud.datastore.LongValue.of; import static com.google.gcloud.datastore.StringValue.of; -import com.google.api.client.util.Preconditions; import com.google.api.services.datastore.DatastoreV1; +import com.google.common.base.MoreObjects; +import com.google.common.base.MoreObjects.ToStringHelper; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.protobuf.InvalidProtocolBufferException; @@ -23,9 +25,10 @@ public abstract class StructuredQuery extends Query { private static final long serialVersionUID = 546838955624019594L; + private static final String KEY_PROPERTY_NAME = "__key__"; private final transient String kind; - private final ImmutableList projection; + private final ImmutableList projection; private final transient Filter filter; private final ImmutableList groupBy; private final transient ImmutableList orderBy; @@ -34,7 +37,6 @@ public abstract class StructuredQuery extends Query { private final transient Integer offset; private final transient Integer limit; - public abstract static class Filter implements Serializable { private static final long serialVersionUID = -6443285436239990860L; @@ -43,6 +45,13 @@ public abstract static class Filter implements Serializable { } protected abstract DatastoreV1.Filter toPb(); + + static Filter fromPb(DatastoreV1.Filter filterPb) { + if (filterPb.hasCompositeFilter()) { + return CompositeFilter.fromPb(filterPb.getCompositeFilter()); + } + return PropertyFilter.fromPb(filterPb.getPropertyFilter()); + } } public static final class CompositeFilter extends Filter { @@ -57,6 +66,10 @@ enum Operator { DatastoreV1.CompositeFilter.Operator toPb() { return DatastoreV1.CompositeFilter.Operator.valueOf(name()); } + + static Operator fromPb(DatastoreV1.CompositeFilter.Operator operatorPb) { + return valueOf(operatorPb.name()); + } } private CompositeFilter(Operator operator, Filter first, Filter... other) { @@ -65,6 +78,21 @@ private CompositeFilter(Operator operator, Filter first, Filter... other) { ImmutableList.builder().add(first).addAll(Arrays.asList(other)).build(); } + private CompositeFilter(Operator operator, ImmutableList filters) { + this.operator = operator; + this.filters = filters; + Preconditions.checkArgument(!filters.isEmpty(), "filters list must not be empty"); + } + + static CompositeFilter fromPb(DatastoreV1.CompositeFilter compositeFilterPb) { + Operator operator = Operator.fromPb(compositeFilterPb.getOperator()); + ImmutableList.Builder filters = ImmutableList.builder(); + for (DatastoreV1.Filter filterPb : compositeFilterPb.getFilterList()) { + filters.add(Filter.fromPb(filterPb)); + } + return new CompositeFilter(operator, filters.build()); + } + public static CompositeFilter and(Filter first, Filter... other) { return new CompositeFilter(Operator.AND, first, other); } @@ -95,17 +123,15 @@ enum Operator { GREATER_THAN, GREATER_THAN_OR_EQUAL, EQUAL, - HAS_ANCESTOR, - IS_NULL { - @Override - public DatastoreV1.PropertyFilter.Operator toPb() { - return DatastoreV1.PropertyFilter.Operator.valueOf(EQUAL.name()); - } - }; - - public DatastoreV1.PropertyFilter.Operator toPb() { + HAS_ANCESTOR; + + DatastoreV1.PropertyFilter.Operator toPb() { return DatastoreV1.PropertyFilter.Operator.valueOf(name()); } + + static Operator fromPb(DatastoreV1.PropertyFilter.Operator operatorPb) { + return valueOf(operatorPb.name()); + } } private PropertyFilter(String property, Operator operator, Value value) { @@ -114,10 +140,11 @@ private PropertyFilter(String property, Operator operator, Value value) { this.value = checkNotNull(value); } - private PropertyFilter(String property, Operator operator) { - this.property = checkNotNull(property); - this.operator = checkNotNull(operator); - this.value = null; + public static PropertyFilter fromPb(DatastoreV1.PropertyFilter propertyFilterPb) { + String property = propertyFilterPb.getProperty().getName(); + Operator operator = Operator.fromPb(propertyFilterPb.getOperator()); + Value value = Value.fromPb(propertyFilterPb.getValue()); + return new PropertyFilter(property, operator, value); } @Override @@ -299,12 +326,12 @@ public static PropertyFilter eq(String property, Blob value) { return new PropertyFilter(property, Operator.EQUAL, of(value)); } - public static PropertyFilter hasAncestor(String property) { - return new PropertyFilter(property, Operator.HAS_ANCESTOR); + public static PropertyFilter hasAncestor(String property, Key key) { + return new PropertyFilter(property, Operator.HAS_ANCESTOR, of(key)); } public static PropertyFilter isNull(String property) { - return new PropertyFilter(property, Operator.IS_NULL, NullValue.of()); + return new PropertyFilter(property, Operator.EQUAL, NullValue.of()); } @Override @@ -328,7 +355,16 @@ public static final class OrderBy implements Serializable { private final Direction direction; public enum Direction { - ASC, DESC + + ASCENDING, DESCENDING; + + DatastoreV1.PropertyOrder.Direction toPb() { + return DatastoreV1.PropertyOrder.Direction.valueOf(name()); + } + + static Direction fromPb(DatastoreV1.PropertyOrder.Direction directionPb) { + return valueOf(directionPb.name()); + } } public OrderBy(String property, Direction direction) { @@ -362,21 +398,120 @@ public Direction direction() { return direction; } + DatastoreV1.PropertyOrder toPb() { + return DatastoreV1.PropertyOrder.newBuilder() + .setDirection(direction.toPb()) + .setProperty(DatastoreV1.PropertyReference.newBuilder().setName(property).build()) + .build(); + } + public static OrderBy asc(String property) { - return new OrderBy(property, OrderBy.Direction.ASC); + return new OrderBy(property, OrderBy.Direction.ASCENDING); } public static OrderBy desc(String property) { - return new OrderBy(property, OrderBy.Direction.DESC); + return new OrderBy(property, OrderBy.Direction.DESCENDING); + } + + static OrderBy fromPb(DatastoreV1.PropertyOrder propertyOrderPb) { + String property = propertyOrderPb.getProperty().getName(); + Direction direction = Direction.fromPb(propertyOrderPb.getDirection()); + return new OrderBy(property, direction); + } + } + + public static final class Projection implements Serializable { + + private static final long serialVersionUID = 3083707957256279470L; + + private final Aggregate aggregate; + private final String property; + + public enum Aggregate { + + FIRST; + + DatastoreV1.PropertyExpression.AggregationFunction toPb() { + return DatastoreV1.PropertyExpression.AggregationFunction.valueOf(name()); + } + + static Aggregate fromPb(DatastoreV1.PropertyExpression.AggregationFunction aggregatePb) { + return valueOf(aggregatePb.name()); + } + } + + private Projection(Aggregate aggregate, String property) { + this.aggregate = aggregate; + this.property = property; + } + + @Override + public int hashCode() { + return Objects.hash(property, aggregate); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Projection)) { + return false; + } + Projection other = (Projection) obj; + return Objects.equals(property, other.property) + && Objects.equals(aggregate, other.aggregate); + } + + @Override + public String toString() { + ToStringHelper toStringHelper = MoreObjects.toStringHelper(this); + toStringHelper.add("property", property); + if (aggregate != null) { + toStringHelper.add("aggregate", aggregate); + } + return toStringHelper.toString(); + } + + DatastoreV1.PropertyExpression toPb() { + DatastoreV1.PropertyExpression.Builder expressionPb = + DatastoreV1.PropertyExpression.newBuilder(); + if (aggregate != null) { + expressionPb.setAggregationFunction(aggregate.toPb()); + } + expressionPb.setProperty( + DatastoreV1.PropertyReference.newBuilder().setName(property).build()); + return expressionPb.build(); + } + + public static Projection fromPb(DatastoreV1.PropertyExpression propertyExpressionPb) { + String property = propertyExpressionPb.getProperty().getName(); + Aggregate aggregate = null; + if (propertyExpressionPb.hasAggregationFunction()) { + aggregate = Aggregate.fromPb(propertyExpressionPb.getAggregationFunction()); + } + return new Projection(aggregate, property); + } + + public static Projection property(String property) { + return new Projection(null, property); + } + + public static Projection aggregate(Aggregate aggregate, String property) { + return new Projection(aggregate, property); + } + + public static Projection first(String property) { + return new Projection(Aggregate.FIRST, property); } } - abstract static class BaseBuilder> { + static abstract class BaseBuilder> { private ResultClass resultClass; private String namespace; private String kind; - private List projection = new LinkedList<>(); + private List projection = new LinkedList<>(); private Filter filter; private List groupBy = new LinkedList<>(); private List orderBy = new LinkedList<>(); @@ -409,7 +544,7 @@ public B startCursor(Cursor startCursor) { return self(); } - public B encCursor(Cursor endCursor) { + public B endCursor(Cursor endCursor) { this.endCursor = endCursor; return self(); } @@ -455,15 +590,15 @@ protected B clearProjection() { return self(); } - protected B projection(String property, String... others) { + protected B projection(Projection projection, Projection... others) { clearProjection(); - addProjection(property, others); + addProjection(projection, others); return self(); } - protected B addProjection(String property, String... others) { - this.projection.add(property); - for (String other : others) { + protected B addProjection(Projection projection, Projection... others) { + this.projection.add(projection); + for (Projection other : others) { this.projection.add(other); } return self(); @@ -488,6 +623,37 @@ protected B addGroupBy(String property, String... others) { return self(); } + protected B mergeFrom(DatastoreV1.Query queryPb) { + if (queryPb.getKindCount() > 0) { + kind(queryPb.getKind(0).getName()); + } + if (queryPb.hasStartCursor()) { + startCursor(new Cursor(queryPb.getStartCursor())); + } + if (queryPb.hasEndCursor()) { + endCursor(new Cursor(queryPb.getEndCursor())); + } + if (queryPb.hasOffset()) { + offset(queryPb.getOffset()); + } + if (queryPb.hasLimit()) { + limit(queryPb.getLimit()); + } + if (queryPb.hasFilter()) { + filter(Filter.fromPb(queryPb.getFilter())); + } + for (DatastoreV1.PropertyOrder orderByPb : queryPb.getOrderList()) { + addOrderBy(OrderBy.fromPb(orderByPb)); + } + for (DatastoreV1.PropertyExpression projectionPb : queryPb.getProjectionList()) { + addProjection(Projection.fromPb(projectionPb)); + } + for (DatastoreV1.PropertyReference groupByPb : queryPb.getGroupByList()) { + addGroupBy(groupByPb.getName()); + } + return self(); + } + public abstract StructuredQuery build(); } @@ -499,6 +665,8 @@ public static final class FullQueryBuilder extends BaseBuilder } @Override - public List projection() { + public List projection() { return super.projection(); } @@ -644,10 +813,10 @@ public String kind() { } protected boolean keyOnly() { - return projection.size() == 1 && projection.get(0).equals("__key__"); + return projection.size() == 1 && projection.get(0).property.equals(KEY_PROPERTY_NAME); } - protected List projection() { + protected List projection() { return projection; } @@ -663,7 +832,7 @@ public ImmutableList orderBy() { return orderBy; } - public Cursor etartCursor() { + public Cursor startCursor() { return startCursor; } @@ -685,7 +854,7 @@ protected void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb) { } @Override - protected GqlQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) { + protected StructuredQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) { // TODO: implment throw new UnsupportedOperationException("paging not implemented yet"); } @@ -717,14 +886,29 @@ protected DatastoreV1.Query toPb() { if (filter != null) { queryPb.setFilter(filter.toPb()); } - // TODO: projection, groupBy, orderBy (or keys-only) + for (OrderBy value : orderBy) { + queryPb.addOrder(value.toPb()); + } + for (String value : groupBy) { + queryPb.addGroupBy(DatastoreV1.PropertyReference.newBuilder().setName(value).build()); + } + for (Projection value : projection) { + queryPb.addProjection(value.toPb()); + } return queryPb.build(); } - static StructuredQuery fromPb(ResultClass resultType, String namespace, + static StructuredQuery fromPb(ResultClass resultClass, String namespace, DatastoreV1.Query queryPb) { - // TODO: implement - return null; + BaseBuilder builder; + if (resultClass.equals(ResultClass.full())) { + builder = new FullQueryBuilder(); + } else if (resultClass.equals(ResultClass.keyOnly())) { + builder = new KeyOnlyQueryBuilder(); + } else { + builder = new ProjectionQueryBuilder(); + } + return builder.namespace(namespace).mergeFrom(queryPb).build(); } public static FullQueryBuilder builder() { diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 4c9f1fdad3c8..79c08cffa674 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -8,6 +8,7 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; import com.google.gcloud.datastore.Query.ResultClass; +import com.google.gcloud.datastore.StructuredQuery.Projection; import com.google.gcloud.datastore.Value.Type; import org.junit.Test; @@ -44,7 +45,7 @@ public class SerializationTest { private static final Query QUERY2 = StructuredQuery.keyOnlyBuilder().kind("k").filter( StructuredQuery.PropertyFilter.eq("p1", "hello")).build(); private static final Query QUERY3 = - StructuredQuery.projectionBuilder().kind("k").projection("p").build(); + StructuredQuery.projectionBuilder().kind("k").projection(Projection.property("p")).build(); private static final KeyValue KEY_VALUE = KeyValue.of(KEY1); private static final NullValue NULL_VALUE = NullValue.builder().indexed(true).build(); private static final StringValue STRING_VALUE = StringValue.of("hello"); From 0e4ec0896bdf7893cc946e9a8209fa2f69f6eb9d Mon Sep 17 00:00:00 2001 From: aozarov Date: Mon, 15 Dec 2014 18:06:17 -0800 Subject: [PATCH 056/771] complete query and start adding tests/javadoc --- .classpath | 10 ++ .../com/google/gcloud/datastore/GqlQuery.java | 109 ++++++------ .../gcloud/datastore/PartialEntity.java | 6 +- .../com/google/gcloud/datastore/Query.java | 30 ++-- .../google/gcloud/datastore/QueryResult.java | 2 +- .../gcloud/datastore/QueryResultImpl.java | 34 ++-- .../gcloud/datastore/StructuredQuery.java | 156 +++++++++++------- .../datastore/DatastoreServiceTest.java | 69 +++++++- .../gcloud/datastore/SerializationTest.java | 24 ++- 9 files changed, 292 insertions(+), 148 deletions(-) diff --git a/.classpath b/.classpath index 9ed6ee4d0713..d6cf6121af66 100644 --- a/.classpath +++ b/.classpath @@ -23,5 +23,15 @@
+ + + + + + + + + + diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java index d6827e7d523e..ff8633760f87 100644 --- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java +++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java @@ -24,9 +24,33 @@ /** * A Google Cloud Datastore GQL. * + *

A usage example:

+ * + * When the type of the results is known the preferred usage would be: + *
 {@code
+ *   Query query = GqlQuery.builder(ResultClass.full(), "select * from kind").build();
+ *   QueryResult results = datastore.runQuery(query);
+ *   while (results.hasNext()) {
+ *     Entity entity = results.next();
+ *     ...
+ *   }
+ * } 
+ * + * When the type of the results is unknown you can use this approach: + *
 {@code
+ *   Query query = GqlQuery.builder("select __key__ from kind").build();
+ *   QueryResult results = datastore.runQuery(query);
+ *   if (Key.class.isAssignableFrom(results.resultClass())) {
+ *     QueryResult keys = (QueryResult) results;
+ *     while (keys.hasNext()) {
+ *       Key key = keys.next();
+ *       ...
+ *     }
+ *   }
+ * } 
* @see GQL Reference */ -public final class GqlQuery extends Query { +public final class GqlQuery extends Query { private static final long serialVersionUID = 5988280590929540569L; @@ -114,132 +138,132 @@ static Argument fromPb(DatastoreV1.GqlQueryArg argPb) { /** * A GQL query builder. */ - public static final class Builder { + public static final class Builder { - private final ResultClass resultClass; + private final ResultClass resultClass; private String namespace; private String queryString; private boolean allowLiteral; private Map nameArgs = new TreeMap<>(); private List numberArgs = new LinkedList<>(); - Builder(ResultClass resultClass, String query) { + Builder(ResultClass resultClass, String query) { this.resultClass = checkNotNull(resultClass); queryString = checkNotNull(query); } - public Builder query(String query) { + public Builder query(String query) { queryString = checkNotNull(query); return this; } - public Builder namespace(String namespace) { + public Builder namespace(String namespace) { this.namespace = validateNamespace(namespace); return this; } - public Builder allowLiteral(boolean allowLiteral) { + public Builder allowLiteral(boolean allowLiteral) { this.allowLiteral = allowLiteral; return this; } - public Builder clearArguments() { + public Builder clearArguments() { nameArgs.clear(); numberArgs.clear(); return this; } - public Builder setArgument(String name, Cursor cursor) { + public Builder setArgument(String name, Cursor cursor) { nameArgs.put(name, new Argument(name, cursor)); return this; } - public Builder setArgument(String name, String... value) { + public Builder setArgument(String name, String... value) { nameArgs.put(name, toArgument(name, StringValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder setArgument(String name, long... value) { + public Builder setArgument(String name, long... value) { nameArgs.put(name, toArgument(name, LongValue.MARSHALLER, Longs.asList(value))); return this; } - public Builder setArgument(String name, double... value) { + public Builder setArgument(String name, double... value) { nameArgs.put(name, toArgument(name, DoubleValue.MARSHALLER, Doubles.asList(value))); return this; } - public Builder setArgument(String name, boolean... value) { + public Builder setArgument(String name, boolean... value) { nameArgs.put(name, toArgument(name, BooleanValue.MARSHALLER, Booleans.asList(value))); return this; } - public Builder setArgument(String name, DateTime... value) { + public Builder setArgument(String name, DateTime... value) { nameArgs.put(name, toArgument(name, DateTimeValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder setArgument(String name, Key... value) { + public Builder setArgument(String name, Key... value) { nameArgs.put(name, toArgument(name, KeyValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder setArgument(String name, PartialEntity... value) { + public Builder setArgument(String name, PartialEntity... value) { nameArgs.put(name, toArgument(name, EntityValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder setArgument(String name, Blob... value) { + public Builder setArgument(String name, Blob... value) { nameArgs.put(name, toArgument(name, BlobValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(Cursor cursor) { + public Builder addArgument(Cursor cursor) { numberArgs.add(new Argument(null, cursor)); return this; } - public Builder addArgument(String... value) { + public Builder addArgument(String... value) { numberArgs.add(toArgument(StringValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(long... value) { + public Builder addArgument(long... value) { numberArgs.add(toArgument(LongValue.MARSHALLER, Longs.asList(value))); return this; } - public Builder addArgument(double... value) { + public Builder addArgument(double... value) { numberArgs.add(toArgument(DoubleValue.MARSHALLER, Doubles.asList(value))); return this; } - public Builder addArgument(boolean... value) { + public Builder addArgument(boolean... value) { numberArgs.add(toArgument(BooleanValue.MARSHALLER, Booleans.asList(value))); return this; } - public Builder addArgument(DateTime... value) { + public Builder addArgument(DateTime... value) { numberArgs.add(toArgument(DateTimeValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(Key... value) { + public Builder addArgument(Key... value) { numberArgs.add(toArgument(KeyValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(PartialEntity... value) { + public Builder addArgument(PartialEntity... value) { numberArgs.add(toArgument(EntityValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(Blob... value) { + public Builder addArgument(Blob... value) { numberArgs.add(toArgument(BlobValue.MARSHALLER, Arrays.asList(value))); return this; } - public GqlQuery build() { + public GqlQuery build() { return new GqlQuery<>(this); } @@ -267,7 +291,7 @@ private static Argument toArgument(String name, Value.BuilderFactory builderFact } } - private GqlQuery(Builder builder) { + private GqlQuery(Builder builder) { super(builder.resultClass, builder.namespace); queryString = builder.queryString; allowLiteral = builder.allowLiteral; @@ -346,35 +370,20 @@ protected void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb) { } @Override - protected GqlQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) { - throw new UnsupportedOperationException("paging not implemented yet"); - /* - // TODO: THIS IS A MAJOR HACK, remove when possible. see b/18705483 - String PREFIX_GROUP = "\\s*(?SELECT .*?)"; - String OFFSET_GROUP = - "(\\s+OFFSET\\s+(?[^\\s]+)(\\s+\\+\\s+(?[^\\s]+))?)?"; - String LIMIT_GROUP = - "(\\s+LIMIT\\s+((?[^\\s]+)|FIRST \\((?[^,]+,[^\\)]+)\\)))?\\s*"; - Pattern pattern = - Pattern.compile(PREFIX_GROUP + OFFSET_GROUP + LIMIT_GROUP, Pattern.CASE_INSENSITIVE); - - Matcher matcher = pattern.matcher(queryString); - - if (!matcher.matches()) { - throw new UnsupportedOperationException("paging for this query is not implemented yet"); - } - */ + protected GqlQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) { + // See b/18705483 + throw new UnsupportedOperationException("paging for this query is not implemented yet"); } @Override - protected Object fromPb(ResultClass resultType, String namespace, byte[] bytesPb) + protected Object fromPb(ResultClass resultType, String namespace, byte[] bytesPb) throws InvalidProtocolBufferException { return fromPb(resultType, namespace, DatastoreV1.GqlQuery.parseFrom(bytesPb)); } - static GqlQuery fromPb(ResultClass resultType, String namespace, + static GqlQuery fromPb(ResultClass resultType, String namespace, DatastoreV1.GqlQuery queryPb) { - Builder builder = new Builder<>(resultType, queryPb.getQueryString()); + Builder builder = new Builder<>(resultType, queryPb.getQueryString()); builder.namespace(namespace); if (queryPb.hasAllowLiteral()) { builder.allowLiteral = queryPb.getAllowLiteral(); @@ -404,7 +413,7 @@ public static GqlQuery.Builder builder(String gql) { * * @see GQL Reference */ - public static GqlQuery.Builder builder(ResultClass resultClass, String gql) { + public static GqlQuery.Builder builder(ResultClass resultClass, String gql) { return new GqlQuery.Builder<>(resultClass, gql); } } diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java index 41cabfccb0ff..b1c3dac6e970 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java @@ -47,7 +47,11 @@ protected PartialEntity(PartialKey key, ImmutableSortedMap> pro this.key = key; } - public Entity newEntity(Key key) { + /** + * Returns a new {@link #Entity} with the same properties as this one and + * with the given {@code key}. + */ + public Entity toEntity(Key key) { return new Entity(key, ImmutableSortedMap.>copyOf(properties())); } diff --git a/src/main/java/com/google/gcloud/datastore/Query.java b/src/main/java/com/google/gcloud/datastore/Query.java index 4959289e67ec..03df75c00312 100644 --- a/src/main/java/com/google/gcloud/datastore/Query.java +++ b/src/main/java/com/google/gcloud/datastore/Query.java @@ -9,21 +9,21 @@ import com.google.protobuf.InvalidProtocolBufferException; -// TODO(ozarov): add a usage examples (gql and regular) /** * A Google Cloud Datastore query. + * For usage examples see {@link GqlQuery} and {@link StructuredQuery}. * - * @param the type of the values returned by this query. + * @param the type of the values returned by this query. * @see Datastore Queries */ -public abstract class Query extends Serializable { +public abstract class Query extends Serializable { private static final long serialVersionUID = -2748141759901313101L; - private final ResultClass resultClass; + private final ResultClass resultClass; private final String namespace; - public static class ResultClass implements java.io.Serializable { + public static class ResultClass implements java.io.Serializable { private static final long serialVersionUID = 2104157695425806623L; private static final ResultClass UNKNOWN = new ResultClass<>(Object.class); @@ -32,13 +32,13 @@ public static class ResultClass implements java.io.Serializable { private static final ResultClass PROJECTION = new ResultClass<>(PartialEntity.class); - private final Class value; + private final Class value; - private ResultClass(Class value) { + private ResultClass(Class value) { this.value = checkNotNull(value); } - public Class value() { + public Class value() { return value; } @@ -131,22 +131,22 @@ Key convert(DatastoreV1.Entity value) { } @Override - ResultClass resultClass() { - return ResultClass.projection(); + ResultClass resultClass() { + return ResultClass.keyOnly(); } }; - abstract T convert(DatastoreV1.Entity value); + abstract V convert(DatastoreV1.Entity value); abstract ResultClass resultClass(); } - Query(ResultClass resultClass, String namespace) { + Query(ResultClass resultClass, String namespace) { this.resultClass = checkNotNull(resultClass); this.namespace = namespace; } - ResultClass getResultClass() { + ResultClass resultClass() { return resultClass; } @@ -167,10 +167,10 @@ protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { return fromPb(resultClass, namespace, bytesPb); } - protected abstract Object fromPb(ResultClass resultClass, String namespace, byte[] bytesPb) + protected abstract Object fromPb(ResultClass resultClass, String namespace, byte[] bytesPb) throws InvalidProtocolBufferException; protected abstract void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb); - protected abstract Query nextQuery(DatastoreV1.QueryResultBatch responsePb); + protected abstract Query nextQuery(DatastoreV1.QueryResultBatch responsePb); } diff --git a/src/main/java/com/google/gcloud/datastore/QueryResult.java b/src/main/java/com/google/gcloud/datastore/QueryResult.java index c569fc7bd361..4c69f6b73eaa 100644 --- a/src/main/java/com/google/gcloud/datastore/QueryResult.java +++ b/src/main/java/com/google/gcloud/datastore/QueryResult.java @@ -9,7 +9,7 @@ * * @param V the type of the results value. */ -public interface QueryResult extends Iterator { +public interface QueryResult extends Iterator { /** * Returns the actual class of the result's values. diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java index 3f81a7fd0010..eecf6a196e6c 100644 --- a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java +++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java @@ -14,11 +14,13 @@ class QueryResultImpl extends AbstractIterator implements QueryResult { private final DatastoreServiceImpl datastore; private final DatastoreV1.ReadOptions readOptionsPb; + private final DatastoreV1.PartitionId partitionIdPb; private final Query.ResultClass resultClass; private Query query; private Query.Type type; private DatastoreV1.QueryResultBatch resultPb; private Iterator entityResultPbIter; + //private ByteString cursor; // only available in v1beta3 static { ImmutableMap.Builder builder = @@ -34,7 +36,15 @@ class QueryResultImpl extends AbstractIterator implements QueryResult { this.datastore = datastore; this.readOptionsPb = readOptionsPb; this.query = query; - this.resultClass = query.getResultClass(); + resultClass = query.resultClass(); + DatastoreV1.PartitionId.Builder pbBuilder = DatastoreV1.PartitionId.newBuilder(); + pbBuilder.setDatasetId(datastore.options().dataset()); + if (query.namespace() != null) { + pbBuilder.setNamespace(query.namespace()); + } else if (datastore.options().namespace() != null) { + pbBuilder.setNamespace(datastore.options().namespace()); + } + partitionIdPb = pbBuilder.build(); sendRequest(); } @@ -43,18 +53,11 @@ private DatastoreV1.QueryResultBatch sendRequest() { if (readOptionsPb != null) { requestPb.setReadOptions(readOptionsPb); } - DatastoreV1.PartitionId.Builder partitionIdPb = DatastoreV1.PartitionId.newBuilder(); - partitionIdPb.setDatasetId(datastore.options().dataset()); - String namespace = query.namespace() != null - ? query.namespace() - : datastore.options().namespace(); - if (namespace != null) { - partitionIdPb.setNamespace(namespace); - } - requestPb.setPartitionId(partitionIdPb.build()); + requestPb.setPartitionId(partitionIdPb); query.populatePb(requestPb); resultPb = datastore.runQuery(requestPb.build()).getBatch(); entityResultPbIter = resultPb.getEntityResultList().iterator(); + // cursor = resultPb.getSkippedCursor(); // only available in v1beta3 type = RESULT_TYPE_CONVERTER.get(resultPb.getEntityResultType()); Preconditions.checkState(resultClass.isAssignableFrom(type.resultClass()), "Unexpected result type"); @@ -68,9 +71,12 @@ protected T computeNext() { query = query.nextQuery(resultPb); sendRequest(); } - return entityResultPbIter.hasNext() - ? type.convert(entityResultPbIter.next().getEntity()) - : endOfData(); + if (!entityResultPbIter.hasNext()) { + return endOfData(); + } + DatastoreV1.EntityResult entityResultPb = entityResultPbIter.next(); + //cursor = entityResultPb.getCursor(); // only available in v1beta3 + return type.convert(entityResultPb.getEntity()); } @Override @@ -80,7 +86,7 @@ public Class resultClass() { @Override public Cursor cursor() { - // TODO(ozarov): implement when v1beta3 is available + //return new Cursor(cursor); // only available in v1beta3 return null; } } diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java index a4d92290e964..dc4a14727e2e 100644 --- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java +++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java @@ -22,7 +22,7 @@ import java.util.List; import java.util.Objects; -public abstract class StructuredQuery extends Query { +public class StructuredQuery extends Query { private static final long serialVersionUID = 546838955624019594L; private static final String KEY_PROPERTY_NAME = "__key__"; @@ -34,7 +34,7 @@ public abstract class StructuredQuery extends Query { private final transient ImmutableList orderBy; private final transient Cursor startCursor; private final transient Cursor endCursor; - private final transient Integer offset; + private final transient int offset; private final transient Integer limit; public abstract static class Filter implements Serializable { @@ -84,6 +84,32 @@ private CompositeFilter(Operator operator, ImmutableList filters) { Preconditions.checkArgument(!filters.isEmpty(), "filters list must not be empty"); } + @Override + public String toString() { + ToStringHelper toStringHelper = MoreObjects.toStringHelper(this); + toStringHelper.add("operator", operator); + toStringHelper.add("filters", filters); + return toStringHelper.toString(); + } + + @Override + public int hashCode() { + return Objects.hash(operator, filters); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof CompositeFilter)) { + return false; + } + CompositeFilter other = (CompositeFilter) obj; + return operator.equals(other.operator) + && filters.equals(other.filters); + } + static CompositeFilter fromPb(DatastoreV1.CompositeFilter compositeFilterPb) { Operator operator = Operator.fromPb(compositeFilterPb.getOperator()); ImmutableList.Builder filters = ImmutableList.builder(); @@ -147,6 +173,15 @@ public static PropertyFilter fromPb(DatastoreV1.PropertyFilter propertyFilterPb) return new PropertyFilter(property, operator, value); } + @Override + public String toString() { + ToStringHelper toStringHelper = MoreObjects.toStringHelper(this); + toStringHelper.add("property", property); + toStringHelper.add("operator", operator); + toStringHelper.add("value", value); + return toStringHelper.toString(); + } + @Override public int hashCode() { return Objects.hash(property, operator, value); @@ -506,9 +541,9 @@ public static Projection first(String property) { } } - static abstract class BaseBuilder> { + static class BaseBuilder> { - private ResultClass resultClass; + private ResultClass resultClass; private String namespace; private String kind; private List projection = new LinkedList<>(); @@ -517,10 +552,10 @@ static abstract class BaseBuilder> { private List orderBy = new LinkedList<>(); private Cursor startCursor; private Cursor endCursor; - private Integer offset; + private int offset; private Integer limit; - BaseBuilder(ResultClass resultClass) { + BaseBuilder(ResultClass resultClass) { this.resultClass = resultClass; } @@ -549,14 +584,14 @@ public B endCursor(Cursor endCursor) { return self(); } - public B offset(Integer offset) { - Preconditions.checkArgument(offset == null || offset >= 0, "offset must not be negative"); + public B offset(int offset) { + Preconditions.checkArgument(offset >= 0, "offset must not be negative"); this.offset = offset; return self(); } public B limit(Integer limit) { - Preconditions.checkArgument(limit == null || offset > 0, "limit must be positive"); + Preconditions.checkArgument(limit == null || limit > 0, "limit must be positive"); this.limit = limit; return self(); } @@ -654,41 +689,43 @@ protected B mergeFrom(DatastoreV1.Query queryPb) { return self(); } - public abstract StructuredQuery build(); + public StructuredQuery build() { + return new StructuredQuery<>(this); + } } - public static final class FullQueryBuilder extends BaseBuilder { + public static final class Builder extends BaseBuilder> { - FullQueryBuilder() { - super(ResultClass.full()); - } - - @Override - public FullQuery build() { - clearProjection(); - clearGroupBy(); - return new FullQuery(this); + public Builder(ResultClass resultClass) { + super(resultClass); } } - public static final class KeyOnlyQueryBuilder extends BaseBuilder { + public static final class KeyOnlyBuilder extends BaseBuilder { - public KeyOnlyQueryBuilder() { + public KeyOnlyBuilder() { super(ResultClass.keyOnly()); + projection(Projection.property(KEY_PROPERTY_NAME)); } @Override - public KeyOnlyQuery build() { + protected KeyOnlyBuilder mergeFrom(DatastoreV1.Query queryPb) { + super.mergeFrom(queryPb); projection(Projection.property(KEY_PROPERTY_NAME)); clearGroupBy(); + return this; + } + + @Override + public KeyOnlyQuery build() { return new KeyOnlyQuery(this); } } - public static final class ProjectionQueryBuilder - extends BaseBuilder { + public static final class ProjectionBuilder + extends BaseBuilder { - public ProjectionQueryBuilder() { + public ProjectionBuilder() { super(ResultClass.projection()); } @@ -698,50 +735,41 @@ public ProjectionQuery build() { } @Override - public ProjectionQueryBuilder clearProjection() { + public ProjectionBuilder clearProjection() { return super.clearProjection(); } @Override - public ProjectionQueryBuilder projection(Projection projection, Projection... others) { + public ProjectionBuilder projection(Projection projection, Projection... others) { return super.projection(projection, others); } @Override - public ProjectionQueryBuilder addProjection(Projection projection, Projection... others) { + public ProjectionBuilder addProjection(Projection projection, Projection... others) { return super.addProjection(projection, others); } @Override - public ProjectionQueryBuilder clearGroupBy() { + public ProjectionBuilder clearGroupBy() { return super.clearGroupBy(); } @Override - public ProjectionQueryBuilder groupBy(String property, String... others) { + public ProjectionBuilder groupBy(String property, String... others) { return super.groupBy(property, others); } @Override - public ProjectionQueryBuilder addGroupBy(String property, String... others) { + public ProjectionBuilder addGroupBy(String property, String... others) { return super.addGroupBy(property, others); } } - public static final class FullQuery extends StructuredQuery { - - private static final long serialVersionUID = -84461800292593840L; - - FullQuery(FullQueryBuilder builder) { - super(builder); - } - } - public static final class KeyOnlyQuery extends StructuredQuery { private static final long serialVersionUID = -7502917784216095473L; - KeyOnlyQuery(KeyOnlyQueryBuilder builder) { + KeyOnlyQuery(KeyOnlyBuilder builder) { super(builder); } } @@ -750,7 +778,7 @@ public static final class ProjectionQuery extends StructuredQuery private static final long serialVersionUID = -3333183044486150649L; - ProjectionQuery(ProjectionQueryBuilder builder) { + ProjectionQuery(ProjectionBuilder builder) { super(builder); Preconditions.checkState(!keyOnly(), "Projection query can't project only '__key__', use KeyOnlyQuery instead."); @@ -767,7 +795,7 @@ public List groupBy() { } } - StructuredQuery(BaseBuilder builder) { + StructuredQuery(BaseBuilder builder) { super(builder.resultClass, builder.namespace); kind = builder.kind; projection = ImmutableList.copyOf(builder.projection); @@ -840,7 +868,7 @@ public Cursor endCursor() { return endCursor; } - public Integer offset() { + public int offset() { return offset; } @@ -854,13 +882,23 @@ protected void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb) { } @Override - protected StructuredQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) { - // TODO: implment - throw new UnsupportedOperationException("paging not implemented yet"); + protected StructuredQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) { + Builder builder = new Builder<>(resultClass()); + builder.mergeFrom(toPb()); + builder.startCursor(new Cursor(responsePb.getEndCursor())); + if (offset > 0 && responsePb.getSkippedResults() < offset) { + builder.offset(offset - responsePb.getSkippedResults()); + } else { + builder.offset(0); + if (limit != null) { + builder.limit(limit - responsePb.getEntityResultCount()); + } + } + return builder.build(); } @Override - protected Object fromPb(ResultClass resultClass, String namespace, byte[] bytesPb) + protected Object fromPb(ResultClass resultClass, String namespace, byte[] bytesPb) throws InvalidProtocolBufferException { return fromPb(resultClass, namespace, DatastoreV1.Query.parseFrom(bytesPb)); } @@ -877,7 +915,7 @@ protected DatastoreV1.Query toPb() { if (endCursor != null) { queryPb.setEndCursor(endCursor.byteString()); } - if (offset != null) { + if (offset > 0) { queryPb.setOffset(offset); } if (limit != null) { @@ -902,24 +940,24 @@ static StructuredQuery fromPb(ResultClass resultClass, String namespace, DatastoreV1.Query queryPb) { BaseBuilder builder; if (resultClass.equals(ResultClass.full())) { - builder = new FullQueryBuilder(); + builder = builder(); } else if (resultClass.equals(ResultClass.keyOnly())) { - builder = new KeyOnlyQueryBuilder(); + builder = keyOnlyBuilder(); } else { - builder = new ProjectionQueryBuilder(); + builder = projectionBuilder(); } return builder.namespace(namespace).mergeFrom(queryPb).build(); } - public static FullQueryBuilder builder() { - return new FullQueryBuilder(); + public static Builder builder() { + return new Builder<>(ResultClass.full()); } - public static KeyOnlyQueryBuilder keyOnlyBuilder() { - return new KeyOnlyQueryBuilder(); + public static KeyOnlyBuilder keyOnlyBuilder() { + return new KeyOnlyBuilder(); } - public static ProjectionQueryBuilder projectionBuilder() { - return new ProjectionQueryBuilder(); + public static ProjectionBuilder projectionBuilder() { + return new ProjectionBuilder(); } } diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index 60525f39f894..b46dee10bde0 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -8,6 +8,9 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.gcloud.datastore.Query.ResultClass; +import com.google.gcloud.datastore.StructuredQuery.OrderBy; + import org.junit.Before; import org.junit.Test; @@ -242,17 +245,75 @@ public void testNewBatchWriter() { @Test public void testRunGqlQueryNoCasting() { - fail("Not yet implemented"); + Query query1 = GqlQuery.builder(ResultClass.full(), "select * from " + KIND1).build(); + QueryResult results1 = datastore.runQuery(query1); + assertTrue(results1.hasNext()); + assertEquals(ENTITY1, results1.next()); + assertFalse(results1.hasNext()); + + datastore.put(ENTITY3); + Query query2 = GqlQuery.builder( + ResultClass.full(), "select * from " + KIND2 + " order by __key__").build(); + QueryResult results2 = datastore.runQuery(query2); + assertTrue(results2.hasNext()); + assertEquals(ENTITY2, results2.next()); + assertTrue(results2.hasNext()); + assertEquals(ENTITY3, results2.next()); + assertFalse(results2.hasNext()); + + query1 = GqlQuery.builder(ResultClass.full(), "select * from bla").build(); + results1 = datastore.runQuery(query1); + assertFalse(results1.hasNext()); + + Query keyOnlyQuery = + GqlQuery.builder(ResultClass.keyOnly(), "select __key__ from " + KIND1).build(); + QueryResult keyOnlyResults = datastore.runQuery(keyOnlyQuery); + assertTrue(keyOnlyResults.hasNext()); + assertEquals(KEY1, keyOnlyResults.next()); + assertFalse(keyOnlyResults.hasNext()); + + Query projectionQuery = GqlQuery.builder( + ResultClass.projection(), "select str from " + KIND1).build(); + QueryResult projectionResult = datastore.runQuery(projectionQuery); + assertTrue(projectionResult.hasNext()); + PartialEntity partialEntity = projectionResult.next(); + assertEquals("str", partialEntity.getString("str")); + assertEquals(1, partialEntity.names().size()); + assertFalse(projectionResult.hasNext()); } @Test public void testRunGqlQueryWithCasting() { - fail("Not yet implemented"); + @SuppressWarnings("unchecked") + Query query1 = (Query) GqlQuery.builder("select * from " + KIND1).build(); + QueryResult results1 = datastore.runQuery(query1); + assertTrue(results1.hasNext()); + assertEquals(ENTITY1, results1.next()); + assertFalse(results1.hasNext()); + + Query query2 = GqlQuery.builder("select * from " + KIND1).build(); + QueryResult results2 = datastore.runQuery(query2); + assertEquals(Entity.class, results2.resultClass()); + @SuppressWarnings("unchecked") + QueryResult results3 = (QueryResult) results2; + assertTrue(results3.hasNext()); + assertEquals(ENTITY1, results3.next()); + assertFalse(results3.hasNext()); } @Test - public void testRunStructuredQueryFull() { - fail("Not yet implemented"); + public void testRunStructuredQuery() { + StructuredQuery query = + StructuredQuery.builder().kind(KIND1).orderBy(OrderBy.asc("__key__")).build(); + QueryResult results1 = datastore.runQuery(query); + assertTrue(results1.hasNext()); + assertEquals(ENTITY1, results1.next()); + assertFalse(results1.hasNext()); + + StructuredQuery keyOnlyQuery = StructuredQuery.keyOnlyBuilder().kind(KIND1).build(); + + + // todo(ozarov): construct a test to very nextQuery/pagination } @Test diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 79c08cffa674..f66bb6bc319a 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -8,7 +8,10 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; import com.google.gcloud.datastore.Query.ResultClass; +import com.google.gcloud.datastore.StructuredQuery.CompositeFilter; +import com.google.gcloud.datastore.StructuredQuery.OrderBy; import com.google.gcloud.datastore.StructuredQuery.Projection; +import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; import com.google.gcloud.datastore.Value.Type; import org.junit.Test; @@ -29,6 +32,7 @@ public class SerializationTest { private static final DateTime DATE_TIME1 = DateTime.now(); private static final Blob BLOB1 = Blob.copyFrom(UTF_8.encode("hello world")); private static final Cursor CURSOR1 = Cursor.copyFrom(new byte[] {1,2}); + private static final Cursor CURSOR2 = Cursor.copyFrom(new byte[] {10}); private static final Query GQL1 = GqlQuery.builder("select * from kind1 where name = @name and age > @1") .setArgument("name", "name1") @@ -42,10 +46,22 @@ public class SerializationTest { .namespace("ns1") .build(); private static final Query QUERY1 = StructuredQuery.builder().kind("kind1").build(); - private static final Query QUERY2 = StructuredQuery.keyOnlyBuilder().kind("k").filter( - StructuredQuery.PropertyFilter.eq("p1", "hello")).build(); - private static final Query QUERY3 = - StructuredQuery.projectionBuilder().kind("k").projection(Projection.property("p")).build(); + private static final Query QUERY2 = StructuredQuery.keyOnlyBuilder() + .kind("k") + .filter(PropertyFilter.eq("p1", "hello")) + .build(); + private static final Query QUERY3 = StructuredQuery.projectionBuilder() + .kind("k") + .namespace("ns1") + .projection(Projection.property("p")) + .limit(100) + .offset(5) + .startCursor(CURSOR1) + .endCursor(CURSOR2) + .filter(CompositeFilter.and(PropertyFilter.gt("p1", 10), PropertyFilter.eq("a", "v"))) + .addGroupBy("p") + .addOrderBy(OrderBy.asc("p")) + .build(); private static final KeyValue KEY_VALUE = KeyValue.of(KEY1); private static final NullValue NULL_VALUE = NullValue.builder().indexed(true).build(); private static final StringValue STRING_VALUE = StringValue.of("hello"); From 86625f83b117123a1ef8d793796910029a0842f0 Mon Sep 17 00:00:00 2001 From: ozarov Date: Mon, 15 Dec 2014 21:41:07 -0800 Subject: [PATCH 057/771] add more tests and javadoc for queries --- .../google/gcloud/datastore/BaseEntity.java | 10 ++-- .../com/google/gcloud/datastore/GqlQuery.java | 6 +- .../gcloud/datastore/StructuredQuery.java | 50 +++++++++++++--- .../datastore/DatastoreServiceTest.java | 59 +++++++++++++++---- 4 files changed, 98 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/BaseEntity.java b/src/main/java/com/google/gcloud/datastore/BaseEntity.java index 606630a5b10c..6d5784839e94 100644 --- a/src/main/java/com/google/gcloud/datastore/BaseEntity.java +++ b/src/main/java/com/google/gcloud/datastore/BaseEntity.java @@ -68,11 +68,6 @@ public B set(String name, Value value) { return self(); } - public B setNull(String name) { - properties.put(name, of()); - return self(); - } - public B set(String name, String value) { properties.put(name, of(value)); return self(); @@ -123,6 +118,11 @@ public B set(String name, Blob value) { return self(); } + public B setNull(String name) { + properties.put(name, of()); + return self(); + } + public E build() { return build(ImmutableSortedMap.copyOf(properties)); } diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java index ff8633760f87..f8769d8ac0ae 100644 --- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java +++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java @@ -26,7 +26,7 @@ * *

A usage example:

* - * When the type of the results is known the preferred usage would be: + *

When the type of the results is known the preferred usage would be: *

 {@code
  *   Query query = GqlQuery.builder(ResultClass.full(), "select * from kind").build();
  *   QueryResult results = datastore.runQuery(query);
@@ -36,7 +36,7 @@
  *   }
  * } 
* - * When the type of the results is unknown you can use this approach: + *

When the type of the results is unknown you can use this approach: *

 {@code
  *   Query query = GqlQuery.builder("select __key__ from kind").build();
  *   QueryResult results = datastore.runQuery(query);
@@ -48,6 +48,8 @@
  *     }
  *   }
  * } 
+ * + * @param the type of the result values this query will produce * @see GQL Reference */ public final class GqlQuery extends Query { diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java index dc4a14727e2e..f9a733d1a529 100644 --- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java +++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java @@ -22,6 +22,40 @@ import java.util.List; import java.util.Objects; +/** + * An implementation of a Google Cloud Datastore Query that can be constructed by providing + * all the specific query elements. + * + *

A usage example:

+ * + *

A simple query that returns all entities for a specific kind + *

 {@code
+ *   StructuredQuery query = StructuredQuery.builder().kind(kind).build();
+ *   QueryResult results = datastore.runQuery(query);
+ *   while (results.hasNext()) {
+ *     Entity entity = results.next();
+ *     ...
+ *   }
+ * } 
+ * + *

A less trivial example of a projection query that returns the first 10 results + * of "age" and "name" properties (sorted and grouped by "age") with an age greater than 18 + *

 {@code
+ *   StructuredQuery query = StructuredQuery.projectionBuilder()
+ *       .kind(kind)
+ *       .projection(Projection.property("age"), Projection.first("name"))
+ *       .filter(PropertyFilter.gt("age", 18))
+ *       .groupBy("age")
+ *       .orderBy(OrderBy.asc("age"))
+ *       .limit(10)
+ *       .build();
+ *   QueryResult results = datastore.runQuery(query);
+ *   ...
+ * } 
+ * + * @param the type of the result values this query will produce + * @see Datastore queries + */ public class StructuredQuery extends Query { private static final long serialVersionUID = 546838955624019594L; @@ -361,8 +395,8 @@ public static PropertyFilter eq(String property, Blob value) { return new PropertyFilter(property, Operator.EQUAL, of(value)); } - public static PropertyFilter hasAncestor(String property, Key key) { - return new PropertyFilter(property, Operator.HAS_ANCESTOR, of(key)); + public static PropertyFilter hasAncestor(Key key) { + return new PropertyFilter(KEY_PROPERTY_NAME, Operator.HAS_ANCESTOR, of(key)); } public static PropertyFilter isNull(String property) { @@ -897,12 +931,6 @@ protected StructuredQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) return builder.build(); } - @Override - protected Object fromPb(ResultClass resultClass, String namespace, byte[] bytesPb) - throws InvalidProtocolBufferException { - return fromPb(resultClass, namespace, DatastoreV1.Query.parseFrom(bytesPb)); - } - @Override protected DatastoreV1.Query toPb() { DatastoreV1.Query.Builder queryPb = DatastoreV1.Query.newBuilder(); @@ -936,6 +964,12 @@ protected DatastoreV1.Query toPb() { return queryPb.build(); } + @Override + protected Object fromPb(ResultClass resultClass, String namespace, byte[] bytesPb) + throws InvalidProtocolBufferException { + return fromPb(resultClass, namespace, DatastoreV1.Query.parseFrom(bytesPb)); + } + static StructuredQuery fromPb(ResultClass resultClass, String namespace, DatastoreV1.Query queryPb) { BaseBuilder builder; diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index b46dee10bde0..e7584afa7a83 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -10,6 +10,8 @@ import com.google.gcloud.datastore.Query.ResultClass; import com.google.gcloud.datastore.StructuredQuery.OrderBy; +import com.google.gcloud.datastore.StructuredQuery.Projection; +import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; import org.junit.Before; import org.junit.Test; @@ -48,7 +50,7 @@ public class DatastoreServiceTest { .set("bool", BOOL_VALUE).set("partial1", EntityValue.of(PARTIAL_ENTITY1)) .set("list", LIST_VALUE2).build(); private static final Entity ENTITY2 = Entity.builder(ENTITY1).key(KEY2).remove("str") - .setNull("null").build(); + .set("name", "koko").setNull("null").set("age", 20).build(); private static final Entity ENTITY3 = Entity.builder(ENTITY1).key(KEY3).remove("str") .set("null", NULL_VALUE).set("partial1", PARTIAL_ENTITY2).set("partial2", ENTITY2).build(); @@ -138,7 +140,29 @@ public void testTransactionWithRead() { @Test public void testTransactionWithQuery() { - fail("not implemented"); + Query query = + StructuredQuery.builder().kind(KIND2).filter(PropertyFilter.hasAncestor(KEY2)).build(); + Transaction transaction = datastore.newTransaction(); + QueryResult results = transaction.runQuery(query); + assertEquals(ENTITY2, results.next()); + assertFalse(results.hasNext()); + transaction.add(ENTITY3); + transaction.commit(); + assertEquals(ENTITY3, datastore.get(KEY3)); + + transaction = datastore.newTransaction(); + results = transaction.runQuery(query); + assertEquals(ENTITY2, results.next()); + transaction.delete(ENTITY3.key()); + // update entity2 during the transaction + datastore.put(Entity.builder(ENTITY2).clear().build()); + try { + transaction.commit(); + fail("Expecting a failure"); + } catch (DatastoreServiceException expected) { + expected.printStackTrace(); + assertEquals(DatastoreServiceException.Code.ABORTED, expected.code()); + } } @Test @@ -311,19 +335,30 @@ public void testRunStructuredQuery() { assertFalse(results1.hasNext()); StructuredQuery keyOnlyQuery = StructuredQuery.keyOnlyBuilder().kind(KIND1).build(); + QueryResult results2 = datastore.runQuery(keyOnlyQuery); + assertTrue(results2.hasNext()); + assertEquals(ENTITY1.key(), results2.next()); + assertFalse(results2.hasNext()); + StructuredQuery projectionQuery = StructuredQuery.projectionBuilder() + .kind(KIND2) + .projection(Projection.property("age"), Projection.first("name")) + .filter(PropertyFilter.gt("age", 18)) + .groupBy("age") + .orderBy(OrderBy.asc("age")) + .limit(10) + .build(); - // todo(ozarov): construct a test to very nextQuery/pagination - } - - @Test - public void testRunStructuredQueryProjection() { - fail("Not yet implemented"); - } + QueryResult results3 = datastore.runQuery(projectionQuery); + assertTrue(results3.hasNext()); + PartialEntity entity = results3.next(); + assertEquals(ENTITY2.key(), entity.key()); + assertEquals(20, entity.getLong("age")); + assertEquals("koko", entity.getString("name")); + assertEquals(2, entity.properties().size()); + assertFalse(results3.hasNext()); - @Test - public void testRunStructuredQueryKeysOnly() { - fail("Not yet implemented"); + // TODO(ozarov): construct a test to very nextQuery/pagination } @Test From bcb9a5981650638fa93ca68c9142d4996b31926c Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 16 Dec 2014 08:46:18 -0800 Subject: [PATCH 058/771] minor comments update --- .../com/google/gcloud/datastore/DatastoreServiceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index e7584afa7a83..fadd24147c67 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -260,7 +260,7 @@ public void testNewBatchWriter() { assertNull(entities.next()); assertFalse(entities.hasNext()); - // TODO need to cover the cases of: + // TODO need to cover the following use-cases: // delete after put/add/update // put after delete/add/update // update after delete/add/put @@ -358,7 +358,7 @@ public void testRunStructuredQuery() { assertEquals(2, entity.properties().size()); assertFalse(results3.hasNext()); - // TODO(ozarov): construct a test to very nextQuery/pagination + // TODO(ozarov): construct a test to verify nextQuery/pagination } @Test From 26a1d81c459c2e0e7320f4ef83756476f9c04bb6 Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 16 Dec 2014 17:37:33 -0800 Subject: [PATCH 059/771] 1) minor changes to datastore. 2) migrate exception handler from AE gcs client 3) start with gcs client. --- pom.xml | 272 ++++++++++++++++-- .../com/google/gcloud/ExceptionHandler.java | 197 +++++++++++++ .../java/com/google/gcloud/RetryHelper.java | 228 +++++++++++++++ .../java/com/google/gcloud/RetryParams.java | 272 ++++++++++++++++++ .../com/google/gcloud/ServiceOptions.java | 2 + .../datastore/DatastoreServiceOptions.java | 9 +- .../google/gcloud/datastore/package-info.java | 5 +- .../java/com/google/gcloud/storage/Acl.java | 5 + .../com/google/gcloud/storage/Bucket.java | 35 +++ .../google/gcloud/storage/StorageService.java | 9 + .../gcloud/storage/StorageServiceFactory.java | 19 ++ .../gcloud/storage/StorageServiceImpl.java | 28 ++ .../gcloud/storage/StorageServiceOptions.java | 100 +++++++ .../google/gcloud/storage/package-info.java | 6 + .../google/gcloud/ExceptionHandlerTest.java | 113 ++++++++ .../com/google/gcloud/RetryParamsTest.java | 91 ++++++ 16 files changed, 1369 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/google/gcloud/ExceptionHandler.java create mode 100644 src/main/java/com/google/gcloud/RetryHelper.java create mode 100644 src/main/java/com/google/gcloud/RetryParams.java create mode 100644 src/main/java/com/google/gcloud/storage/Acl.java create mode 100644 src/main/java/com/google/gcloud/storage/Bucket.java create mode 100644 src/main/java/com/google/gcloud/storage/StorageService.java create mode 100644 src/main/java/com/google/gcloud/storage/StorageServiceFactory.java create mode 100644 src/main/java/com/google/gcloud/storage/StorageServiceImpl.java create mode 100644 src/main/java/com/google/gcloud/storage/StorageServiceOptions.java create mode 100644 src/main/java/com/google/gcloud/storage/package-info.java create mode 100644 src/test/java/com/google/gcloud/ExceptionHandlerTest.java create mode 100644 src/test/java/com/google/gcloud/RetryParamsTest.java diff --git a/pom.xml b/pom.xml index 3a573d91459f..585f21738fa5 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,6 @@ - + + 4.0.0 com.ozarov.testing git-demo @@ -7,59 +9,295 @@ com.google.http-client google-http-client - 1.18.0-rc + 1.19.0 + compile com.google.oauth-client google-oauth-client - 1.18.0-rc + 1.19.0 + compile com.google.guava guava RELEASE + compile com.google.apis - - google-api-services-datastore-protobuf - + google-api-services-datastore-protobuf v1beta2-rev1-2.1.0 + compile com.google.api-client google-api-client-appengine - 1.18.0-rc + 1.19.0 + compile junit junit + RELEASE test + + + joda-time + joda-time RELEASE + compile - joda-time - joda-time - RELEASE + org.json + json + 20090211 + compile - org.json - json - 20090211 + com.google.apis + google-api-services-storage + v1-rev23-1.19.0 + compile + + + com.google.apis + google-api-services-datastore + v1beta2-rev23-1.19.0 + + + + false + + central + Central Repository + http://repo.maven.apache.org/maven2 + + + + + + never + + + false + + central + Central Repository + http://repo.maven.apache.org/maven2 + + + /usr/local/google/home/ozarov/git/git-demo/src/main/java + /usr/local/google/home/ozarov/git/git-demo/src/main/scripts + /usr/local/google/home/ozarov/git/git-demo/src/test/java + /usr/local/google/home/ozarov/git/git-demo/target/classes + /usr/local/google/home/ozarov/git/git-demo/target/test-classes + + + /usr/local/google/home/ozarov/git/git-demo/src/main/resources + + + + + /usr/local/google/home/ozarov/git/git-demo/src/test/resources + + + /usr/local/google/home/ozarov/git/git-demo/target + git-demo-0.0.1-SNAPSHOT + + + + maven-antrun-plugin + 1.3 + + + maven-assembly-plugin + 2.2-beta-5 + + + maven-dependency-plugin + 2.8 + + + maven-release-plugin + 2.3.2 + + + - org.apache.maven.plugins maven-compiler-plugin 3.1 + + + default-testCompile + test-compile + + testCompile + + + 1.7 + 1.7 + UTF-8 + + + + default-compile + compile + + compile + + + 1.7 + 1.7 + UTF-8 + + + - 1.7 - 1.7 - UTF-8 + 1.7 + 1.7 + UTF-8 + + + + maven-clean-plugin + 2.5 + + + default-clean + clean + + clean + + + + + + maven-install-plugin + 2.4 + + + default-install + install + + install + + + + + + maven-resources-plugin + 2.6 + + + default-resources + process-resources + + resources + + + + default-testResources + process-test-resources + + testResources + + + + + + maven-surefire-plugin + 2.12.4 + + + default-test + test + + test + + + + + + maven-jar-plugin + 2.4 + + + default-jar + package + + jar + + + + + + maven-deploy-plugin + 2.7 + + + default-deploy + deploy + + deploy + + + + + + maven-site-plugin + 3.3 + + + default-site + site + + site + + + /usr/local/google/home/ozarov/git/git-demo/target/site + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + + + default-deploy + site-deploy + + deploy + + + /usr/local/google/home/ozarov/git/git-demo/target/site + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + + + /usr/local/google/home/ozarov/git/git-demo/target/site + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + + /usr/local/google/home/ozarov/git/git-demo/target/site + diff --git a/src/main/java/com/google/gcloud/ExceptionHandler.java b/src/main/java/com/google/gcloud/ExceptionHandler.java new file mode 100644 index 000000000000..7a7371e175ab --- /dev/null +++ b/src/main/java/com/google/gcloud/ExceptionHandler.java @@ -0,0 +1,197 @@ +package com.google.gcloud; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.Set; +import java.util.concurrent.Callable; + +/** + * Exception handling used by {@link RetryHelper}. + */ +public final class ExceptionHandler implements Serializable { + + private static final long serialVersionUID = -2460707015779532919L; + + private static final ExceptionHandler DEFAULT_INSTANCE = + builder().retryOn(Exception.class).abortOn(RuntimeException.class).build(); + + private final ImmutableSet> retriableExceptions; + private final ImmutableSet> nonRetriableExceptions; + private final Set retryInfos = Sets.newHashSet(); + + /** + * ExceptionHandler builder. + */ + public static class Builder { + + private final ImmutableSet.Builder> retriableExceptions = + new ImmutableSet.Builder<>(); + private final ImmutableSet.Builder> nonRetriableExceptions = + new ImmutableSet.Builder<>(); + + private Builder() { + } + + /** + * Specify on what exceptions to continue. + * + * @param exceptions retry should continue when such exceptions are thrown + * @return the Builder for chaining + */ + @SafeVarargs + public final Builder retryOn(Class... exceptions) { + for (Class exception : exceptions) { + retriableExceptions.add(checkNotNull(exception)); + } + return this; + } + + /** + * Specify on what exceptions to abort. + * + * @param exceptions retry should abort when such exceptions are thrown + * @return the Builder for chaining + */ + @SafeVarargs + public final Builder abortOn(Class... exceptions) { + for (Class exception : exceptions) { + nonRetriableExceptions.add(checkNotNull(exception)); + } + return this; + } + + /** + * Returns a new ExceptionHandler instance. + */ + public ExceptionHandler build() { + return new ExceptionHandler(this); + } + } + + @VisibleForTesting + static final class RetryInfo implements Serializable { + + private static final long serialVersionUID = -4264634837841455974L; + private final Class exception; + private final boolean retry; + private final Set children = Sets.newHashSet(); + + RetryInfo(Class exception, boolean retry) { + this.exception = checkNotNull(exception); + this.retry = retry; + } + + @Override + public int hashCode() { + return exception.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof RetryInfo)) { + return false; + } + // We only care about exception in equality as we allow only one instance per exception + return ((RetryInfo) obj).exception.equals(exception); + } + } + + private ExceptionHandler(Builder builder) { + retriableExceptions = builder.retriableExceptions.build(); + nonRetriableExceptions = builder.nonRetriableExceptions.build(); + Preconditions.checkArgument( + Sets.intersection(retriableExceptions, nonRetriableExceptions).isEmpty(), + "Same exception was found in both retriable and non-retriable sets"); + for (Class exception : retriableExceptions) { + addToRetryInfos(retryInfos, new RetryInfo(exception, true)); + } + for (Class exception : nonRetriableExceptions) { + addToRetryInfos(retryInfos, new RetryInfo(exception, false)); + } + } + + private static void addToRetryInfos(Set retryInfos, RetryInfo retryInfo) { + for (RetryInfo current : retryInfos) { + if (current.exception.isAssignableFrom(retryInfo.exception)) { + addToRetryInfos(current.children, retryInfo); + return; + } + if (retryInfo.exception.isAssignableFrom(current.exception)) { + retryInfo.children.add(current); + } + } + retryInfos.removeAll(retryInfo.children); + retryInfos.add(retryInfo); + } + + + private static RetryInfo findMostSpecificRetryInfo(Set retryInfos, + Class exception) { + for (RetryInfo current : retryInfos) { + if (current.exception.isAssignableFrom(exception)) { + RetryInfo match = findMostSpecificRetryInfo(current.children, exception); + return match == null ? current : match; + } + } + return null; + } + + // called for Class, therefore a "call" method must be found. + private static Method getCallableMethod(Class clazz) { + try { + return clazz.getDeclaredMethod("call"); + } catch (NoSuchMethodException e) { + // check parent + return getCallableMethod(clazz.getSuperclass()); + } catch (SecurityException e) { + // This should never happen + throw new RuntimeException("Unexpected exception", e); + } + } + + void verifyCaller(Callable callable) { + Method callMethod = getCallableMethod(callable.getClass()); + for (Class exceptionOrError : callMethod.getExceptionTypes()) { + Preconditions.checkArgument(Exception.class.isAssignableFrom(exceptionOrError), + "Callable method exceptions must be dervied from Exception"); + @SuppressWarnings("unchecked") Class exception = + (Class) exceptionOrError; + Preconditions.checkArgument(findMostSpecificRetryInfo(retryInfos, exception) != null, + "Declared exception '" + exception + "' is not covered by exception handler"); + } + } + + public ImmutableSet> getRetriableExceptions() { + return retriableExceptions; + } + + public ImmutableSet> getNonRetriableExceptions() { + return nonRetriableExceptions; + } + + boolean shouldRetry(Exception ex) { + RetryInfo retryInfo = findMostSpecificRetryInfo(retryInfos, ex.getClass()); + return retryInfo == null ? false : retryInfo.retry; + } + + /** + * Returns an instance which retry any checked exception and abort on any runtime exception. + */ + public static ExceptionHandler getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + public static Builder builder() { + return new Builder(); + } +} diff --git a/src/main/java/com/google/gcloud/RetryHelper.java b/src/main/java/com/google/gcloud/RetryHelper.java new file mode 100644 index 000000000000..ac4489684e3e --- /dev/null +++ b/src/main/java/com/google/gcloud/RetryHelper.java @@ -0,0 +1,228 @@ +package com.google.gcloud; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.Math.min; +import static java.lang.Math.pow; +import static java.lang.Math.random; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +import com.google.common.base.MoreObjects.ToStringHelper; +import com.google.common.base.Stopwatch; + +import java.io.InterruptedIOException; +import java.nio.channels.ClosedByInterruptException; +import java.util.concurrent.Callable; +import java.util.logging.Logger; + +/** + * Utility class for retrying operations. For more details about the parameters, see + * {@link RetryParams}. If the request is never successful, a {@link RetriesExhaustedException} will + * be thrown. + * + * @param return value of the closure that is being run with retries + */ +public class RetryHelper { + + private static final Logger log = Logger.getLogger(RetryHelper.class.getName()); + + private final Stopwatch stopwatch; + private final Callable callable; + private final RetryParams params; + private final ExceptionHandler exceptionHandler; + private int attemptNumber; + + + private static final ThreadLocal context = new ThreadLocal<>(); + + public static class RetryHelperException extends RuntimeException { + + private static final long serialVersionUID = -2907061015610448235L; + + RetryHelperException() {} + + RetryHelperException(String message) { + super(message); + } + + RetryHelperException(Throwable cause) { + super(cause); + } + + RetryHelperException(String message, Throwable cause) { + super(message, cause); + } + } + + /** + * Thrown when a RetryHelper failed to complete its work due to interruption. Throwing this + * exception also sets the thread interrupt flag. + */ + public static final class RetryInterruptedException extends RetryHelperException { + + private static final long serialVersionUID = 1678966737697204885L; + + RetryInterruptedException() {} + + /** + * Sets the caller thread interrupt flag and throws {@code RetryInteruptedException}. + */ + static void propagate() throws RetryInterruptedException { + Thread.currentThread().interrupt(); + throw new RetryInterruptedException(); + } + } + + /** + * Thrown when a RetryHelper has attempted the maximum number of attempts allowed by RetryParams + * and was not successful. + */ + public static final class RetriesExhaustedException extends RetryHelperException { + + private static final long serialVersionUID = 780199686075408083L; + + RetriesExhaustedException(String message) { + super(message); + } + + RetriesExhaustedException(String message, Throwable cause) { + super(message, cause); + } + } + + /** + * Thrown when RetryHelper callable has indicate it should not be retried. + */ + public static final class NonRetriableException extends RetryHelperException { + + private static final long serialVersionUID = -2331878521983499652L; + + NonRetriableException(Throwable ex) { + super(ex); + } + } + + static class Context { + + private final RetryHelper helper; + + Context(RetryHelper helper) { + this.helper = helper; + } + + public RetryParams getRetryParams() { + return helper.params; + } + + public int getAttemptNumber() { + return helper.attemptNumber; + } + } + + @VisibleForTesting + static final void setContext(Context ctx) { + if (ctx == null) { + context.remove(); + } else { + context.set(ctx); + } + } + + static final Context getContext() { + return context.get(); + } + + @VisibleForTesting + RetryHelper(Callable callable, RetryParams params, ExceptionHandler exceptionHandler, + Stopwatch stopwatch) { + this.callable = checkNotNull(callable); + this.params = checkNotNull(params); + this.stopwatch = checkNotNull(stopwatch); + this.exceptionHandler = checkNotNull(exceptionHandler); + exceptionHandler.verifyCaller(callable); + } + + @Override + public String toString() { + ToStringHelper toStringHelper = MoreObjects.toStringHelper(this); + toStringHelper.add("stopwatch", stopwatch); + toStringHelper.add("attempNumber", attemptNumber); + toStringHelper.add("callable", callable); + return toStringHelper.toString(); + } + + private V doRetry() throws RetryHelperException { + stopwatch.start(); + while (true) { + attemptNumber++; + Exception exception; + try { + V value = callable.call(); + if (attemptNumber > 1) { + log.fine(this + ": attempt #" + attemptNumber + " succeeded"); + } + return value; + } catch (Exception e) { + if (!exceptionHandler.shouldRetry(e)) { + if (e instanceof InterruptedException || e instanceof InterruptedIOException + || e instanceof ClosedByInterruptException) { + RetryInterruptedException.propagate(); + } + throw new NonRetriableException(e); + } + exception = e; + } + if (attemptNumber >= params.getRetryMaxAttempts() + || attemptNumber >= params.getRetryMinAttempts() + && stopwatch.elapsed(MILLISECONDS) >= params.getTotalRetryPeriodMillis()) { + throw new RetriesExhaustedException(this + ": Too many failures, giving up", exception); + } + long sleepDurationMillis = getSleepDuration(params, attemptNumber); + log.fine(this + ": Attempt #" + attemptNumber + " failed [" + exception + "], sleeping for " + + sleepDurationMillis + " ms"); + try { + Thread.sleep(sleepDurationMillis); + } catch (InterruptedException e) { + RetryInterruptedException.propagate(); + } + } + } + + @VisibleForTesting + static long getSleepDuration(RetryParams retryParams, int attemptsSoFar) { + long initialDelay = retryParams.getInitialRetryDelayMillis(); + double backoffFactor = retryParams.getRetryDelayBackoffFactor(); + long maxDelay = retryParams.getMaxRetryDelayMillis(); + long retryDelay = getExponentialValue(initialDelay, backoffFactor, maxDelay, attemptsSoFar); + return (long) ((random() / 2.0 + .75) * retryDelay); + } + + private static long getExponentialValue(long initialDelay, double backoffFactor, long maxDelay, + int attemptsSoFar) { + return (long) min(maxDelay, pow(backoffFactor, min(1, attemptsSoFar) - 1) * initialDelay); + } + + public static V runWithRetries(Callable callable) throws RetryHelperException { + return runWithRetries(callable, RetryParams.getDefaultInstance(), + ExceptionHandler.getDefaultInstance()); + } + + public static V runWithRetries(Callable callable, RetryParams params, + ExceptionHandler exceptionHandler) throws RetryHelperException { + return runWithRetries(callable, params, exceptionHandler, Stopwatch.createUnstarted()); + } + + @VisibleForTesting + static V runWithRetries(Callable callable, RetryParams params, + ExceptionHandler exceptionHandler, Stopwatch stopwatch) throws RetryHelperException { + RetryHelper retryHelper = new RetryHelper<>(callable, params, exceptionHandler, stopwatch); + Context previousContext = getContext(); + setContext(new Context(retryHelper)); + try { + return retryHelper.doRetry(); + } finally { + setContext(previousContext); + } + } +} diff --git a/src/main/java/com/google/gcloud/RetryParams.java b/src/main/java/com/google/gcloud/RetryParams.java new file mode 100644 index 000000000000..cd500cd4149f --- /dev/null +++ b/src/main/java/com/google/gcloud/RetryParams.java @@ -0,0 +1,272 @@ +package com.google.gcloud; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.MoreObjects; +import com.google.common.base.MoreObjects.ToStringHelper; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Parameters for configuring retries with an exponential backoff. + * Initial request is executed immediately. If the request fails but passes the + * {@link ExceptionHandler} criteria the calling thread sleeps for {@code initialRetryDelayMillis}. + * Each subsequent failure the sleep interval is calculated as: + *

+ * {@code retryDelayBackoffFactor ^ attempts * initialRetryDelayMillis} but would be upper-bounded + * to {@code maxRetryDelayMillis} + *

+ * This proceeds until either the request is successful, {@code retryMaxAttempts} are made, or both + * {@code retryMinAttempts} are made and {@code totalRetryPeriodMillis} have elapsed. To construct + * {@code RetryParams}, first create a {@link RetryParams.Builder}. The builder is mutable and each + * of the parameters can be set (any unset parameters will fallback to the defaults). The + * {@code Builder} can be then used to create an immutable {@code RetryParams} object. For default + * {@code RetryParams} use {@link #getDefaultInstance}. Default settings are subject to change + * release to release. If you require specific settings, explicitly create an instance of + * {@code RetryParams} with all the required settings. + * + * @see RetryHelper + */ +public final class RetryParams implements Serializable { + + private static final long serialVersionUID = -8492751576749007700L; + + public static final int DEFAULT_RETRY_MIN_ATTEMPTS = 3; + public static final int DEFAULT_RETRY_MAX_ATTEMPTS = 6; + public static final long DEFAULT_INITIAL_RETRY_DELAY_MILLIS = 250L; + public static final long DEFAULT_MAX_RETRY_DELAY_MILLIS = 10_000L; + public static final double DEFAULT_RETRY_DELAY_BACKOFF_FACTOR = 2.0; + public static final long DEFAULT_TOTAL_RETRY_PERIOD_MILLIS = 50_000L; + + private final int retryMinAttempts; + private final int retryMaxAttempts; + private final long initialRetryDelayMillis; + private final long maxRetryDelayMillis; + private final double retryDelayBackoffFactor; + private final long totalRetryPeriodMillis; + + private static final RetryParams DEFAULT_INSTANCE = new RetryParams(new Builder()); + + + /** + * Create a new RetryParams with the parameters from a {@link RetryParams.Builder} + * + * @param builder the parameters to use to construct the RetryParams object + */ + private RetryParams(Builder builder) { + retryMinAttempts = builder.retryMinAttempts; + retryMaxAttempts = builder.retryMaxAttempts; + initialRetryDelayMillis = builder.initialRetryDelayMillis; + maxRetryDelayMillis = builder.maxRetryDelayMillis; + retryDelayBackoffFactor = builder.retryDelayBackoffFactor; + totalRetryPeriodMillis = builder.totalRetryPeriodMillis; + checkArgument(retryMinAttempts >= 0, "retryMinAttempts must not be negative"); + checkArgument(retryMaxAttempts >= retryMinAttempts, + "retryMaxAttempts must not be smaller than retryMinAttempts"); + checkArgument(initialRetryDelayMillis >= 0, "initialRetryDelayMillis must not be negative"); + checkArgument(maxRetryDelayMillis >= initialRetryDelayMillis, + "maxRetryDelayMillis must not be smaller than initialRetryDelayMillis"); + checkArgument(retryDelayBackoffFactor >= 0, "retryDelayBackoffFactor must not be negative"); + checkArgument(totalRetryPeriodMillis >= 0, "totalRetryPeriodMillis must not be negative"); + } + + /** + * Returns an instance with the default parameters. + */ + public static RetryParams getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + /** + * RetryParams builder. + */ + public static final class Builder { + + private int retryMinAttempts; + private int retryMaxAttempts; + private long initialRetryDelayMillis; + private long maxRetryDelayMillis; + private double retryDelayBackoffFactor; + private long totalRetryPeriodMillis; + + private Builder() { + this(null); + } + + Builder(/* Nullable */RetryParams retryParams) { + if (retryParams == null) { + retryMinAttempts = DEFAULT_RETRY_MIN_ATTEMPTS; + retryMaxAttempts = DEFAULT_RETRY_MAX_ATTEMPTS; + initialRetryDelayMillis = DEFAULT_INITIAL_RETRY_DELAY_MILLIS; + maxRetryDelayMillis = DEFAULT_MAX_RETRY_DELAY_MILLIS; + retryDelayBackoffFactor = DEFAULT_RETRY_DELAY_BACKOFF_FACTOR; + totalRetryPeriodMillis = DEFAULT_TOTAL_RETRY_PERIOD_MILLIS; + } else { + retryMinAttempts = retryParams.getRetryMinAttempts(); + retryMaxAttempts = retryParams.getRetryMaxAttempts(); + initialRetryDelayMillis = retryParams.getInitialRetryDelayMillis(); + maxRetryDelayMillis = retryParams.getMaxRetryDelayMillis(); + retryDelayBackoffFactor = retryParams.getRetryDelayBackoffFactor(); + totalRetryPeriodMillis = retryParams.getTotalRetryPeriodMillis(); + } + } + + /** + * Sets retryMinAttempts. + * + * @param retryMinAttempts the retryMinAttempts to set + * @return the Builder for chaining + */ + public Builder retryMinAttempts(int retryMinAttempts) { + this.retryMinAttempts = retryMinAttempts; + return this; + } + + /** + * Sets retryMaxAttempts. + * + * @param retryMaxAttempts the retryMaxAttempts to set + * @return the Builder for chaining + */ + public Builder retryMaxAttempts(int retryMaxAttempts) { + this.retryMaxAttempts = retryMaxAttempts; + return this; + } + + /** + * Sets initialRetryDelayMillis. + * + * @param initialRetryDelayMillis the initialRetryDelayMillis to set + * @return the Builder for chaining + */ + public Builder initialRetryDelayMillis(long initialRetryDelayMillis) { + this.initialRetryDelayMillis = initialRetryDelayMillis; + return this; + } + + /** + * Sets maxRetryDelayMillis. + * + * @param maxRetryDelayMillis the maxRetryDelayMillis to set + * @return the Builder for chaining + */ + public Builder maxRetryDelayMillis(long maxRetryDelayMillis) { + this.maxRetryDelayMillis = maxRetryDelayMillis; + return this; + } + + /** + * Sets retryDelayBackoffFactor. + * + * @param retryDelayBackoffFactor the retryDelayBackoffFactor to set + * @return the Builder for chaining + */ + public Builder retryDelayBackoffFactor(double retryDelayBackoffFactor) { + this.retryDelayBackoffFactor = retryDelayBackoffFactor; + return this; + } + + /** + * Sets totalRetryPeriodMillis. + * + * @param totalRetryPeriodMillis the totalRetryPeriodMillis to set + * @return the Builder for chaining + */ + public Builder totalRetryPeriodMillis(long totalRetryPeriodMillis) { + this.totalRetryPeriodMillis = totalRetryPeriodMillis; + return this; + } + + /** + * Create an instance of RetryParams with the parameters set in this builder. + * + * @return a new instance of RetryParams + */ + public RetryParams build() { + return new RetryParams(this); + } + } + + /** + * Returns the retryMinAttempts. + */ + public int getRetryMinAttempts() { + return retryMinAttempts; + } + + /** + * Returns the retryMaxAttempts. + */ + public int getRetryMaxAttempts() { + return retryMaxAttempts; + } + + /** + * Returns the initialRetryDelayMillis. + */ + public long getInitialRetryDelayMillis() { + return initialRetryDelayMillis; + } + + /** + * Returns the maxRetryDelayMillis. + */ + public long getMaxRetryDelayMillis() { + return maxRetryDelayMillis; + } + + /** + * Returns the maxRetryDelayBackoffFactor. + */ + public double getRetryDelayBackoffFactor() { + return retryDelayBackoffFactor; + } + + /** + * Returns the totalRetryPeriodMillis. + */ + public long getTotalRetryPeriodMillis() { + return totalRetryPeriodMillis; + } + + + + @Override + public int hashCode() { + return Objects.hash(retryMinAttempts, retryMaxAttempts, initialRetryDelayMillis, + maxRetryDelayMillis, retryDelayBackoffFactor, totalRetryPeriodMillis); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof RetryParams)) { + return false; + } + RetryParams other = (RetryParams) obj; + return retryMinAttempts == other.retryMinAttempts && retryMaxAttempts == other.retryMaxAttempts + && initialRetryDelayMillis == other.initialRetryDelayMillis + && maxRetryDelayMillis == other.maxRetryDelayMillis + && retryDelayBackoffFactor == other.retryDelayBackoffFactor + && totalRetryPeriodMillis == other.totalRetryPeriodMillis; + } + + @Override + public String toString() { + ToStringHelper toStringHelper = MoreObjects.toStringHelper(this); + toStringHelper.add("retryMinAttempts", retryMinAttempts); + toStringHelper.add("retryMaxAttempts", retryMaxAttempts); + toStringHelper.add("initialRetryDelayMillis", initialRetryDelayMillis); + toStringHelper.add("maxRetryDelayMillis", maxRetryDelayMillis); + toStringHelper.add("retryDelayBackoffFactor", retryDelayBackoffFactor); + toStringHelper.add("totalRetryPeriodMillis", totalRetryPeriodMillis); + return toStringHelper.toString(); + } + + public static Builder builder() { + return new Builder(); + } +} diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java index 826f59e1d300..24510adf7688 100644 --- a/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/src/main/java/com/google/gcloud/ServiceOptions.java @@ -116,4 +116,6 @@ public AuthConfig authConfig() { protected HttpRequestInitializer httpRequestInitializer() { return authConfig().httpRequestInitializer(httpTransport, scopes()); } + + public abstract Builder toBuilder(); } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java index c0d5e3aee3a1..4931bd62af71 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java @@ -95,6 +95,11 @@ protected Set scopes() { return SCOPES; } + @Override + public Builder toBuilder() { + return new Builder(this); + } + DatastoreOptions toDatastoreOptions() { return new DatastoreOptions.Builder() .dataset(dataset()) @@ -106,8 +111,4 @@ DatastoreOptions toDatastoreOptions() { public static Builder builder() { return new Builder(); } - - public static Builder builder(DatastoreServiceOptions options) { - return new Builder(options); - } } diff --git a/src/main/java/com/google/gcloud/datastore/package-info.java b/src/main/java/com/google/gcloud/datastore/package-info.java index b186edb83b0e..7862681038c5 100644 --- a/src/main/java/com/google/gcloud/datastore/package-info.java +++ b/src/main/java/com/google/gcloud/datastore/package-info.java @@ -1,6 +1,7 @@ /** * A client to the Google Cloud Datastore. - * A simple usage example: + * + *

A simple usage example: *

 {@code
  * DatastoreServiceOptions options = DatastoreServiceOptions.builder().dataset(DATASET).build();
  * DatastoreService datastore = DatastoreServiceFactory.getDefault(options);
@@ -29,5 +30,7 @@
  *   }
  * }
  * } 
+ * + * @see Google Cloud Datastore */ package com.google.gcloud.datastore; diff --git a/src/main/java/com/google/gcloud/storage/Acl.java b/src/main/java/com/google/gcloud/storage/Acl.java new file mode 100644 index 000000000000..06f2c467bbaf --- /dev/null +++ b/src/main/java/com/google/gcloud/storage/Acl.java @@ -0,0 +1,5 @@ +package com.google.gcloud.storage; + +public interface Acl { + +} diff --git a/src/main/java/com/google/gcloud/storage/Bucket.java b/src/main/java/com/google/gcloud/storage/Bucket.java new file mode 100644 index 000000000000..fa7bae1da7fd --- /dev/null +++ b/src/main/java/com/google/gcloud/storage/Bucket.java @@ -0,0 +1,35 @@ +package com.google.gcloud.storage; + +import java.nio.ByteBuffer; + +public interface Bucket { + + String name(); + + Acl acl(); + + void updateAcl(Acl acl); + + Acl defaultObjectAcl(); + + void updateDefaultObjectAcl(); + + Acl acl(String objectName); + + void updateAcl(String objectName, Acl acl); + + void delete(String... objectName); + + void compose(Iterable source, String dest); + + void copy(String source, String dest); + + // TODO (ozarov): consider replace with Object that has a reference to bucket and name + // that object can return its own meta-data, update its own meta-data, replace its content + // via a stream or byteBuffer, read its content (via stream or ByteBuffer),... + //void copy(String source, String bucket, String dest); + + void put(String name, ByteBuffer bytes); + + // TODO: add listing +} diff --git a/src/main/java/com/google/gcloud/storage/StorageService.java b/src/main/java/com/google/gcloud/storage/StorageService.java new file mode 100644 index 000000000000..52e2c879c0f8 --- /dev/null +++ b/src/main/java/com/google/gcloud/storage/StorageService.java @@ -0,0 +1,9 @@ +package com.google.gcloud.storage; + +public interface StorageService { + + Iterable listBuckets(); + + Bucket getBucket(String bucket); + +} diff --git a/src/main/java/com/google/gcloud/storage/StorageServiceFactory.java b/src/main/java/com/google/gcloud/storage/StorageServiceFactory.java new file mode 100644 index 000000000000..acc243b9ba38 --- /dev/null +++ b/src/main/java/com/google/gcloud/storage/StorageServiceFactory.java @@ -0,0 +1,19 @@ +package com.google.gcloud.storage; + + + +public abstract class StorageServiceFactory { + + private static final StorageServiceFactory INSTANCE = new StorageServiceFactory() { + @Override + public StorageService get(StorageServiceOptions options) { + return new StorageServiceImpl(options); + } + }; + + public static StorageService getDefault(StorageServiceOptions options) { + return INSTANCE.get(options); + } + + public abstract StorageService get(StorageServiceOptions options); +} diff --git a/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java b/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java new file mode 100644 index 000000000000..135bff666c0f --- /dev/null +++ b/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java @@ -0,0 +1,28 @@ +package com.google.gcloud.storage; + +import com.google.api.services.storage.Storage; + +final class StorageServiceImpl implements StorageService { + + private final StorageServiceOptions options; + private final Storage storage; + + StorageServiceImpl(StorageServiceOptions options) { + this.options = options; + this.storage = options.getStorage(); + } + + @Override + public Iterable listBuckets() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Bucket getBucket(String bucket) { + // TODO Auto-generated method stub + return null; + } + + +} diff --git a/src/main/java/com/google/gcloud/storage/StorageServiceOptions.java b/src/main/java/com/google/gcloud/storage/StorageServiceOptions.java new file mode 100644 index 000000000000..3fa48eb5095c --- /dev/null +++ b/src/main/java/com/google/gcloud/storage/StorageServiceOptions.java @@ -0,0 +1,100 @@ +package com.google.gcloud.storage; + +import com.google.api.client.json.jackson.JacksonFactory; +import com.google.api.services.storage.Storage; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.gcloud.ServiceOptions; + +import java.lang.reflect.Method; +import java.util.Set; + +public class StorageServiceOptions extends ServiceOptions { + + private static final String GCS_SCOPE = "https://www.googleapis.com/auth/devstorage.full_control"; + private static final Set SCOPES = ImmutableSet.of(GCS_SCOPE); + private static final String DEFAULT_PATH_DELIMITER = "/"; + + private final String project; + private final String pathDelimiter; + + StorageServiceOptions(Builder builder) { + super(builder); + pathDelimiter = MoreObjects.firstNonNull(builder.pathDelimiter, DEFAULT_PATH_DELIMITER); + project = builder.project != null ? builder.project : getAppEngineProject(); + Preconditions.checkArgument(project != null, "Missing required project id"); + } + + private static String getAppEngineProject() { + // TODO(ozarov): An alternative to reflection would be to depend on AE api jar: + // http://mvnrepository.com/artifact/com.google.appengine/appengine-api-1.0-sdk/1.2.0 + try { + Class factoryClass = + Class.forName("com.google.appengine.api.appidentity.AppIdentityServiceFactory"); + Method method = factoryClass.getMethod("getAppIdentityService"); + Object appIdentityService = method.invoke(null); + method = appIdentityService.getClass().getMethod("getServiceAccountName"); + String serviceAccountName = (String) method.invoke(appIdentityService); + int indexOfAtSign = serviceAccountName.indexOf('@'); + return serviceAccountName.substring(0, indexOfAtSign); + } catch (Exception ex) { + return null; + } + } + + public static class Builder extends ServiceOptions.Builder { + + private String project; + private String pathDelimiter; + + private Builder() { + } + + private Builder(StorageServiceOptions options) { + super(options); + } + + public Builder project(String project) { + this.project = project; + return this; + } + + public Builder pathDelimiter(String pathDelimiter) { + this.pathDelimiter = pathDelimiter; + return this; + } + + @Override + public StorageServiceOptions build() { + return new StorageServiceOptions(this); + } + } + + @Override + protected Set scopes() { + return SCOPES; + } + + Storage getStorage() { + return new Storage.Builder(httpTransport(), new JacksonFactory(), httpRequestInitializer()) + .build(); + } + + public String pathDelimiter() { + return pathDelimiter; + } + + @Override + public Builder toBuilder() { + return new Builder(this); + } + + public static Builder builder() { + return new Builder(); + } + + public static Builder builder(StorageServiceOptions options) { + return new Builder(options); + } +} diff --git a/src/main/java/com/google/gcloud/storage/package-info.java b/src/main/java/com/google/gcloud/storage/package-info.java new file mode 100644 index 000000000000..0f78d1bab46a --- /dev/null +++ b/src/main/java/com/google/gcloud/storage/package-info.java @@ -0,0 +1,6 @@ +/** + * A client to Google Cloud Storage. + * + * @see Google Cloud Storageg + */ +package com.google.gcloud.storage; diff --git a/src/test/java/com/google/gcloud/ExceptionHandlerTest.java b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java new file mode 100644 index 000000000000..f88c5211ad93 --- /dev/null +++ b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java @@ -0,0 +1,113 @@ +package com.google.gcloud; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.channels.ClosedByInterruptException; +import java.util.concurrent.Callable; + +/** + * Tests for {@link ExceptionHandler}. + */ +@RunWith(JUnit4.class) +public class ExceptionHandlerTest { + + @Test + public void testVerifyCaller() { + class A implements Callable { + @Override + public Object call() throws IOException, InterruptedException { + return null; + } + } + + class B extends A { + } + + class C extends A { + @Override + public Object call() throws FileNotFoundException { + return null; + } + } + + class D extends C { + @Override + public Object call() throws IllegalArgumentException { + return null; + } + } + + class E extends A { + @Override + public String call() throws NullPointerException { + return null; + } + } + + class F extends A { + @Override + public Object call() throws Error { + return null; + } + } + + // using default exception handler (retry upon any non-runtime exceptions) + ExceptionHandler handler = ExceptionHandler.getDefaultInstance(); + assertValidCallable(new A(), handler); + assertValidCallable(new B(), handler); + assertValidCallable(new C(), handler); + assertValidCallable(new D(), handler); + assertValidCallable(new E(), handler); + assertInvalidCallable(new F(), handler); + + handler = ExceptionHandler.builder() + .retryOn(FileNotFoundException.class, NullPointerException.class) + .build(); + assertInvalidCallable(new A(), handler); + assertInvalidCallable(new B(), handler); + assertValidCallable(new C(), handler); + assertInvalidCallable(new D(), handler); + assertValidCallable(new E(), handler); + assertInvalidCallable(new F(), handler); + } + + private static void assertValidCallable(Callable callable, ExceptionHandler handler) { + handler.verifyCaller(callable); + } + + private static void assertInvalidCallable(Callable callable, ExceptionHandler handler) { + try { + handler.verifyCaller(callable); + fail("Expected RetryHelper constructor to fail"); + } catch (IllegalArgumentException ex) { + // expected + } + } + + @Test + public void testShouldTry() { + ExceptionHandler handler = ExceptionHandler.builder().retryOn(IOException.class).build(); + assertTrue(handler.shouldRetry(new IOException())); + assertTrue(handler.shouldRetry(new ClosedByInterruptException())); + assertFalse(handler.shouldRetry(new RuntimeException())); + + handler = ExceptionHandler.builder() + .retryOn(IOException.class, NullPointerException.class) + .abortOn(RuntimeException.class, ClosedByInterruptException.class, + InterruptedException.class) + .build(); + assertTrue(handler.shouldRetry(new IOException())); + assertFalse(handler.shouldRetry(new ClosedByInterruptException())); + assertFalse(handler.shouldRetry(new InterruptedException())); + assertFalse(handler.shouldRetry(new RuntimeException())); + assertTrue(handler.shouldRetry(new NullPointerException())); + } +} diff --git a/src/test/java/com/google/gcloud/RetryParamsTest.java b/src/test/java/com/google/gcloud/RetryParamsTest.java new file mode 100644 index 000000000000..b59324f0cb1e --- /dev/null +++ b/src/test/java/com/google/gcloud/RetryParamsTest.java @@ -0,0 +1,91 @@ +package com.google.gcloud; + +import static com.google.gcloud.RetryParams.DEFAULT_INITIAL_RETRY_DELAY_MILLIS; +import static com.google.gcloud.RetryParams.DEFAULT_MAX_RETRY_DELAY_MILLIS; +import static com.google.gcloud.RetryParams.DEFAULT_RETRY_DELAY_BACKOFF_FACTOR; +import static com.google.gcloud.RetryParams.DEFAULT_RETRY_MAX_ATTEMPTS; +import static com.google.gcloud.RetryParams.DEFAULT_RETRY_MIN_ATTEMPTS; +import static com.google.gcloud.RetryParams.DEFAULT_TOTAL_RETRY_PERIOD_MILLIS; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import com.google.gcloud.RetryParams.Builder; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Arrays; + +/** + * Tests for {@link RetryParams}. + */ +@RunWith(JUnit4.class) +public class RetryParamsTest { + + @Test + public void testDefaults() { + RetryParams params1 = RetryParams.getDefaultInstance(); + RetryParams params2 = RetryParams.builder().build(); + for (RetryParams params : Arrays.asList(params1, params2)) { + assertEquals(DEFAULT_INITIAL_RETRY_DELAY_MILLIS, params.getInitialRetryDelayMillis()); + assertEquals(DEFAULT_MAX_RETRY_DELAY_MILLIS, params.getMaxRetryDelayMillis()); + assertEquals(DEFAULT_RETRY_DELAY_BACKOFF_FACTOR, params.getRetryDelayBackoffFactor(), 0); + assertEquals(DEFAULT_RETRY_MAX_ATTEMPTS, params.getRetryMaxAttempts()); + assertEquals(DEFAULT_RETRY_MIN_ATTEMPTS, params.getRetryMinAttempts()); + assertEquals(DEFAULT_TOTAL_RETRY_PERIOD_MILLIS, params.getTotalRetryPeriodMillis()); + } + } + + @Test + public void testSetAndCopy() { + RetryParams.Builder builder = RetryParams.builder(); + builder.initialRetryDelayMillis(101); + builder.maxRetryDelayMillis(102); + builder.retryDelayBackoffFactor(103); + builder.retryMinAttempts(107); + builder.retryMaxAttempts(108); + builder.totalRetryPeriodMillis(109); + RetryParams params1 = builder.build(); + RetryParams params2 = new RetryParams.Builder(params1).build(); + for (RetryParams params : Arrays.asList(params1, params2)) { + assertEquals(101, params.getInitialRetryDelayMillis()); + assertEquals(102, params.getMaxRetryDelayMillis()); + assertEquals(103, params.getRetryDelayBackoffFactor(), 0); + assertEquals(107, params.getRetryMinAttempts()); + assertEquals(108, params.getRetryMaxAttempts()); + assertEquals(109, params.getTotalRetryPeriodMillis()); + } + } + + @Test + public void testBadSettings() { + RetryParams.Builder builder = RetryParams.builder(); + builder.initialRetryDelayMillis(-1); + builder = verifyFailure(builder); + builder.maxRetryDelayMillis(RetryParams.getDefaultInstance().getInitialRetryDelayMillis() - 1); + builder = verifyFailure(builder); + builder.retryDelayBackoffFactor(-1); + builder = verifyFailure(builder); + builder.retryMinAttempts(-1); + builder = verifyFailure(builder); + builder.retryMaxAttempts(RetryParams.getDefaultInstance().getRetryMinAttempts() - 1); + builder = verifyFailure(builder); + builder.totalRetryPeriodMillis(-1); + builder = verifyFailure(builder); + // verify that it is OK for min and max to be equal + builder.retryMaxAttempts(RetryParams.getDefaultInstance().getRetryMinAttempts()); + builder.maxRetryDelayMillis(RetryParams.getDefaultInstance().getInitialRetryDelayMillis()); + builder.build(); + } + + private static Builder verifyFailure(Builder builder) { + try { + builder.build(); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // expected + } + return RetryParams.builder(); + } +} From 73b11399ff3401f6c716d1c82feaa4a45d85f23b Mon Sep 17 00:00:00 2001 From: Patrick Costello Date: Wed, 17 Dec 2014 14:33:44 -0800 Subject: [PATCH 060/771] Rename comit to commit Fix a typo in a private method. --- .../gcloud/datastore/DatastoreServiceImpl.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index d55aaa47a380..b41fed8b0ee5 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -161,7 +161,7 @@ public void add(Entity... entities) { } mutationPb.addInsert(entity.toPb()); } - comitMutation(mutationPb); + commitMutation(mutationPb); } @Override @@ -174,7 +174,7 @@ public void update(Entity... entities) { for (Entity entity : dedupEntities.values()) { mutationPb.addUpdate(entity.toPb()); } - comitMutation(mutationPb); + commitMutation(mutationPb); } @Override @@ -187,7 +187,7 @@ public void put(Entity... entities) { for (Entity e : dedupEntities.values()) { mutationPb.addUpsert(e.toPb()); } - comitMutation(mutationPb); + commitMutation(mutationPb); } @Override @@ -197,20 +197,20 @@ public void delete(Key... keys) { for (Key key : dedupKeys) { mutationPb.addDelete(key.toPb()); } - comitMutation(mutationPb); + commitMutation(mutationPb); } - private void comitMutation(DatastoreV1.Mutation.Builder mutationPb) { + private void commitMutation(DatastoreV1.Mutation.Builder mutationPb) { if (options.force()) { mutationPb.setForce(true); } DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder(); requestPb.setMode(DatastoreV1.CommitRequest.Mode.NON_TRANSACTIONAL); requestPb.setMutation(mutationPb); - comitMutation(requestPb); + commitMutation(requestPb); } - void comitMutation(DatastoreV1.CommitRequest.Builder requestPb) { + void commitMutation(DatastoreV1.CommitRequest.Builder requestPb) { try { datastore.commit(requestPb.build()); } catch (DatastoreException e) { From 77d2970ba26af8c3b71c8f52c63ae6a558db605d Mon Sep 17 00:00:00 2001 From: ozarov Date: Wed, 17 Dec 2014 15:46:03 -0800 Subject: [PATCH 061/771] Added DatastoreReaderWriter Renamed KeyBuilder utility to KeyFactory Add interceptors to exception handler --- .gitignore | 1 + pom.xml | 198 ------------------ .../com/google/gcloud/ExceptionHandler.java | 66 +++++- .../google/gcloud/datastore/BaseEntity.java | 10 +- .../com/google/gcloud/datastore/BaseKey.java | 17 +- .../gcloud/datastore/BatchWriterImpl.java | 2 +- .../datastore/DatastoreReaderWriter.java | 8 + .../gcloud/datastore/DatastoreService.java | 8 +- .../datastore/DatastoreServiceImpl.java | 5 - .../com/google/gcloud/datastore/Entity.java | 6 +- .../java/com/google/gcloud/datastore/Key.java | 9 +- .../google/gcloud/datastore/KeyBuilder.java | 45 ---- .../google/gcloud/datastore/KeyFactory.java | 45 ++++ .../gcloud/datastore/PartialEntity.java | 6 +- .../google/gcloud/datastore/PartialKey.java | 7 +- .../google/gcloud/datastore/Transaction.java | 4 +- .../google/gcloud/datastore/package-info.java | 2 +- .../com/google/gcloud/storage/Bucket.java | 1 + .../google/gcloud/ExceptionHandlerTest.java | 51 ++++- .../datastore/DatastoreServiceTest.java | 28 +-- 20 files changed, 201 insertions(+), 318 deletions(-) create mode 100644 src/main/java/com/google/gcloud/datastore/DatastoreReaderWriter.java delete mode 100644 src/main/java/com/google/gcloud/datastore/KeyBuilder.java create mode 100644 src/main/java/com/google/gcloud/datastore/KeyFactory.java diff --git a/.gitignore b/.gitignore index b83d22266ac8..3ae51aeeae93 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target/ +.settings diff --git a/pom.xml b/pom.xml index 585f21738fa5..1fa28d56c920 100644 --- a/pom.xml +++ b/pom.xml @@ -90,214 +90,16 @@ - /usr/local/google/home/ozarov/git/git-demo/src/main/java - /usr/local/google/home/ozarov/git/git-demo/src/main/scripts - /usr/local/google/home/ozarov/git/git-demo/src/test/java - /usr/local/google/home/ozarov/git/git-demo/target/classes - /usr/local/google/home/ozarov/git/git-demo/target/test-classes - - - /usr/local/google/home/ozarov/git/git-demo/src/main/resources - - - - - /usr/local/google/home/ozarov/git/git-demo/src/test/resources - - - /usr/local/google/home/ozarov/git/git-demo/target - git-demo-0.0.1-SNAPSHOT - - - - maven-antrun-plugin - 1.3 - - - maven-assembly-plugin - 2.2-beta-5 - - - maven-dependency-plugin - 2.8 - - - maven-release-plugin - 2.3.2 - - - maven-compiler-plugin 3.1 - - - default-testCompile - test-compile - - testCompile - - - 1.7 - 1.7 - UTF-8 - - - - default-compile - compile - - compile - - - 1.7 - 1.7 - UTF-8 - - - 1.7 1.7 UTF-8 - - maven-clean-plugin - 2.5 - - - default-clean - clean - - clean - - - - - - maven-install-plugin - 2.4 - - - default-install - install - - install - - - - - - maven-resources-plugin - 2.6 - - - default-resources - process-resources - - resources - - - - default-testResources - process-test-resources - - testResources - - - - - - maven-surefire-plugin - 2.12.4 - - - default-test - test - - test - - - - - - maven-jar-plugin - 2.4 - - - default-jar - package - - jar - - - - - - maven-deploy-plugin - 2.7 - - - default-deploy - deploy - - deploy - - - - - - maven-site-plugin - 3.3 - - - default-site - site - - site - - - /usr/local/google/home/ozarov/git/git-demo/target/site - - - org.apache.maven.plugins - maven-project-info-reports-plugin - - - - - - default-deploy - site-deploy - - deploy - - - /usr/local/google/home/ozarov/git/git-demo/target/site - - - org.apache.maven.plugins - maven-project-info-reports-plugin - - - - - - - /usr/local/google/home/ozarov/git/git-demo/target/site - - - org.apache.maven.plugins - maven-project-info-reports-plugin - - - - - - /usr/local/google/home/ozarov/git/git-demo/target/site - diff --git a/src/main/java/com/google/gcloud/ExceptionHandler.java b/src/main/java/com/google/gcloud/ExceptionHandler.java index 7a7371e175ab..9ad46f9d1040 100644 --- a/src/main/java/com/google/gcloud/ExceptionHandler.java +++ b/src/main/java/com/google/gcloud/ExceptionHandler.java @@ -4,6 +4,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; @@ -22,25 +23,65 @@ public final class ExceptionHandler implements Serializable { private static final ExceptionHandler DEFAULT_INSTANCE = builder().retryOn(Exception.class).abortOn(RuntimeException.class).build(); + private final ImmutableList interceptors; private final ImmutableSet> retriableExceptions; private final ImmutableSet> nonRetriableExceptions; private final Set retryInfos = Sets.newHashSet(); + public interface Interceptor extends Serializable { + + /** + * This method is called before evaluating if the exception should be propagated + * and could short-circuit the evaluation process. + * + * @param exception the exception that is being evaluated + * @return {@code Boolean.TRUE} if exception should be ignored, {@code Boolean.FALSE} + * if exception should be propagated or {@code null} if evaluation should proceed. + */ + Boolean shouldRetry(Exception exception); + + /** + * This method is called after the evaluation but could alter the result if desired. + * + * @param exception the exception that is being evaluated + * @param shouldRetry the result of the evaluation + * @return {@code true} if exception should be ignored or {@code false} + * if exception should be propagated. + */ + boolean shouldRetry(Exception exception, boolean shouldRetry); + } + /** * ExceptionHandler builder. */ public static class Builder { + private final ImmutableList.Builder interceptors = ImmutableList.builder(); private final ImmutableSet.Builder> retriableExceptions = - new ImmutableSet.Builder<>(); + ImmutableSet.builder(); private final ImmutableSet.Builder> nonRetriableExceptions = - new ImmutableSet.Builder<>(); + ImmutableSet.builder(); private Builder() { } + + /** + * Adds the exception handler interceptors. + * Call order will be maintained. + + * @param interceptors the interceptors for this exception handler + * @return the Builder for chaining + */ + public Builder interceptor(Interceptor... interceptors) { + for (Interceptor interceptor : interceptors) { + this.interceptors.add(interceptor); + } + return this; + } + /** - * Specify on what exceptions to continue. + * Add the exceptions to ignore/retry-on. * * @param exceptions retry should continue when such exceptions are thrown * @return the Builder for chaining @@ -54,7 +95,7 @@ public final Builder retryOn(Class... exceptions) { } /** - * Specify on what exceptions to abort. + * Adds the exceptions to abort on. * * @param exceptions retry should abort when such exceptions are thrown * @return the Builder for chaining @@ -107,6 +148,7 @@ public boolean equals(Object obj) { } private ExceptionHandler(Builder builder) { + interceptors = builder.interceptors.build(); retriableExceptions = builder.retriableExceptions.build(); nonRetriableExceptions = builder.nonRetriableExceptions.build(); Preconditions.checkArgument( @@ -171,17 +213,27 @@ void verifyCaller(Callable callable) { } } - public ImmutableSet> getRetriableExceptions() { + public Set> getRetriableExceptions() { return retriableExceptions; } - public ImmutableSet> getNonRetriableExceptions() { + public Set> getNonRetriableExceptions() { return nonRetriableExceptions; } boolean shouldRetry(Exception ex) { + for (Interceptor interceptor : interceptors) { + Boolean shouldRetry = interceptor.shouldRetry(ex); + if (shouldRetry != null) { + return shouldRetry.booleanValue(); + } + } RetryInfo retryInfo = findMostSpecificRetryInfo(retryInfos, ex.getClass()); - return retryInfo == null ? false : retryInfo.retry; + boolean shouldRetry = retryInfo == null ? false : retryInfo.retry; + for (Interceptor interceptor : interceptors) { + shouldRetry = interceptor.shouldRetry(ex, shouldRetry); + } + return shouldRetry; } /** diff --git a/src/main/java/com/google/gcloud/datastore/BaseEntity.java b/src/main/java/com/google/gcloud/datastore/BaseEntity.java index 6d5784839e94..d5497bb0b308 100644 --- a/src/main/java/com/google/gcloud/datastore/BaseEntity.java +++ b/src/main/java/com/google/gcloud/datastore/BaseEntity.java @@ -30,9 +30,9 @@ abstract class BaseEntity extends Serializable { private final transient ImmutableSortedMap> properties; - protected abstract static class Builder> { + protected abstract static class Builder> { - private final Map> properties; + protected final Map> properties; protected Builder() { properties = new HashMap<>(); @@ -123,11 +123,7 @@ public B setNull(String name) { return self(); } - public E build() { - return build(ImmutableSortedMap.copyOf(properties)); - } - - protected abstract E build(ImmutableSortedMap> properties); + public abstract BaseEntity build(); } protected BaseEntity(ImmutableSortedMap> properties) { diff --git a/src/main/java/com/google/gcloud/datastore/BaseKey.java b/src/main/java/com/google/gcloud/datastore/BaseKey.java index 2812574f6dbe..d8a7a553c157 100644 --- a/src/main/java/com/google/gcloud/datastore/BaseKey.java +++ b/src/main/java/com/google/gcloud/datastore/BaseKey.java @@ -26,12 +26,12 @@ abstract class BaseKey extends Serializable { private final transient ImmutableList ancestors; private final transient String kind; - abstract static class Builder> { + abstract static class Builder> { - private String dataset; - private String namespace; - private String kind; - private final List ancestors; + protected String dataset; + protected String namespace; + protected String kind; + protected final List ancestors; private static final int MAX_PATH = 100; @@ -100,12 +100,7 @@ public B namespace(String namespace) { return self(); } - public K build() { - return build(dataset, namespace, ImmutableList.copyOf(ancestors), kind); - } - - protected abstract K build( - String dataset, String namespace, ImmutableList ancestors, String kind); + protected abstract BaseKey build(); } BaseKey(String dataset, String namespace, ImmutableList ancestors, String kind) { diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java index 2395c2968c54..9f760faee971 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java +++ b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java @@ -119,7 +119,7 @@ public void submit() { } DatastoreV1.CommitRequest.Builder requestPb = newCommitRequest(); requestPb.setMutation(mutationPb); - datastore.comitMutation(requestPb); + datastore.commitMutation(requestPb); active = false; } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreReaderWriter.java b/src/main/java/com/google/gcloud/datastore/DatastoreReaderWriter.java new file mode 100644 index 000000000000..52304f4e7e27 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DatastoreReaderWriter.java @@ -0,0 +1,8 @@ +package com.google.gcloud.datastore; + + +/** + * An interface that combines both Google Cloud Datastore read and write operations. + */ +public interface DatastoreReaderWriter extends DatastoreReader, DatastoreWriter { +} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index efd41bd9544e..45076277f701 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -5,19 +5,13 @@ /** * An interface for Google Cloud Datastore dataset. */ -public interface DatastoreService extends DatastoreReader, DatastoreWriter { +public interface DatastoreService extends DatastoreReaderWriter { /** * Returns the {@code DatastoreServiceOptions} for this service. */ DatastoreServiceOptions options(); - /** - * Returns a key builder for the requested {@code kind}. - * The key would be initialized with the this service dataset and default namespace. - */ - KeyBuilder newKeyBuilder(String kind); - /** * Returns a new Datastore transaction. * diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index b41fed8b0ee5..7af34773c3b8 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -32,11 +32,6 @@ public DatastoreServiceOptions options() { return options; } - @Override - public KeyBuilder newKeyBuilder(String kind) { - return new KeyBuilder(this, kind); - } - @Override public BatchWriter newBatchWriter(BatchWriteOption... batchWriteOption) { return new BatchWriterImpl(this, batchWriteOption); diff --git a/src/main/java/com/google/gcloud/datastore/Entity.java b/src/main/java/com/google/gcloud/datastore/Entity.java index 5262fcccd8c1..2d4ae4306942 100644 --- a/src/main/java/com/google/gcloud/datastore/Entity.java +++ b/src/main/java/com/google/gcloud/datastore/Entity.java @@ -20,7 +20,7 @@ public final class Entity extends PartialEntity { private static final long serialVersionUID = 432961565733066915L; - public static final class Builder extends BaseEntity.Builder { + public static final class Builder extends BaseEntity.Builder { private Key key; @@ -39,8 +39,8 @@ public Builder key(Key key) { } @Override - protected Entity build(ImmutableSortedMap> properties) { - return new Entity(key, properties); + public Entity build() { + return new Entity(key, ImmutableSortedMap.copyOf(properties)); } } diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index 272fc6e4d7f4..a9b7ae6a0965 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -25,7 +25,7 @@ public final class Key extends PartialKey { private final transient PathElement leaf; - public static final class Builder extends BaseKey.Builder { + public static final class Builder extends BaseKey.Builder { private String name; private Long id; @@ -62,12 +62,11 @@ public Builder id(long id) { } @Override - protected Key build(String dataset, String namespace, ImmutableList ancestors, - String kind) { + public Key build() { if (id == null) { - return new Key(dataset, namespace, ancestors, kind, name); + return new Key(dataset, namespace, ImmutableList.copyOf(ancestors), kind, name); } - return new Key(dataset, namespace, ancestors, kind, id); + return new Key(dataset, namespace, ImmutableList.copyOf(ancestors), kind, id); } } diff --git a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java b/src/main/java/com/google/gcloud/datastore/KeyBuilder.java deleted file mode 100644 index a906a29d423c..000000000000 --- a/src/main/java/com/google/gcloud/datastore/KeyBuilder.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.google.gcloud.datastore; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.collect.ImmutableList; - -/** - * An helper for creating keys for a specific {@link DatastoreService}, - * using its associated dataset and namespace. - */ -public final class KeyBuilder extends BaseKey.Builder { - - private final DatastoreService service; - - /** - * Constructing a KeyBuilder. - */ - public KeyBuilder(DatastoreService service, String kind) { - super(checkNotNull(service).options().dataset(), kind); - this.service = service; - namespace(service.options().namespace()); - } - - @Override - protected PartialKey build(String dataset, String namespace, - ImmutableList ancestors, String kind) { - return new PartialKey(dataset, namespace, ancestors, kind); - } - - public Key build(String name) { - return build().newKey(name); - } - - public Key build(long id) { - return build().newKey(id); - } - - /** - * Builds a key with a newly allocated id. - * @throws DatastoreServiceException if allocation failed. - */ - public Key allocateIdAndBuild() { - return service.allocateId(build()); - } -} diff --git a/src/main/java/com/google/gcloud/datastore/KeyFactory.java b/src/main/java/com/google/gcloud/datastore/KeyFactory.java new file mode 100644 index 000000000000..f671e0f72da5 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/KeyFactory.java @@ -0,0 +1,45 @@ +package com.google.gcloud.datastore; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.collect.ImmutableList; + +/** + * An helper for creating keys for a specific {@link DatastoreService}, + * using its associated dataset and namespace. + */ +public final class KeyFactory extends BaseKey.Builder { + + private final DatastoreService service; + + public KeyFactory(DatastoreService service, String kind) { + super(checkNotNull(service).options().dataset(), kind); + this.service = service; + namespace(service.options().namespace()); + } + + @Override + protected PartialKey build() { + return new PartialKey(dataset, namespace, ImmutableList.copyOf(ancestors), kind); + } + + public PartialKey newKey() { + return build(); + } + + public Key newKey(String name) { + return new Key(dataset, namespace, ImmutableList.copyOf(ancestors), kind, name); + } + + public Key newKey(long id) { + return new Key(dataset, namespace, ImmutableList.copyOf(ancestors), kind, id); + } + + /** + * Return a key with a newly allocated id. + * @throws DatastoreServiceException if allocation failed. + */ + public Key allocateId() { + return service.allocateId(build()); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java index b1c3dac6e970..9edefac1ca34 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java @@ -19,7 +19,7 @@ public class PartialEntity extends BaseEntity { private final transient PartialKey key; - public static class Builder extends BaseEntity.Builder { + public static class Builder extends BaseEntity.Builder { private PartialKey key; @@ -37,8 +37,8 @@ public Builder key(PartialKey key) { } @Override - protected PartialEntity build(ImmutableSortedMap> properties) { - return new PartialEntity(key, properties); + public PartialEntity build() { + return new PartialEntity(key, ImmutableSortedMap.copyOf(properties)); } } diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index f7e6c00374c8..7630f3ebf275 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -15,7 +15,7 @@ public class PartialKey extends BaseKey { private static final long serialVersionUID = -75301206578793347L; - public static class Builder extends BaseKey.Builder { + public static class Builder extends BaseKey.Builder { private Builder(String dataset, String kind) { super(dataset, kind); @@ -26,9 +26,8 @@ private Builder(PartialKey copyFrom) { } @Override - protected PartialKey build(String dataset, String namespace, - ImmutableList ancestors, String kind) { - return new PartialKey(dataset, namespace, ancestors, kind); + public PartialKey build() { + return new PartialKey(dataset, namespace, ImmutableList.copyOf(ancestors), kind); } } diff --git a/src/main/java/com/google/gcloud/datastore/Transaction.java b/src/main/java/com/google/gcloud/datastore/Transaction.java index d47a63ddbf1e..2a9267cf6d41 100644 --- a/src/main/java/com/google/gcloud/datastore/Transaction.java +++ b/src/main/java/com/google/gcloud/datastore/Transaction.java @@ -33,7 +33,7 @@ * @see Google Cloud Datastore transactions * */ -public interface Transaction extends DatastoreReader, DatastoreWriter { +public interface Transaction extends DatastoreReaderWriter { /** * {@inheritDoc} @@ -78,6 +78,8 @@ public interface Transaction extends DatastoreReader, DatastoreWriter { /** * Rollback the transaction. + * + * @throws DatastoreServiceException if transaction was already committed */ void rollback(); diff --git a/src/main/java/com/google/gcloud/datastore/package-info.java b/src/main/java/com/google/gcloud/datastore/package-info.java index 7862681038c5..3babfd370e11 100644 --- a/src/main/java/com/google/gcloud/datastore/package-info.java +++ b/src/main/java/com/google/gcloud/datastore/package-info.java @@ -5,7 +5,7 @@ *
 {@code
  * DatastoreServiceOptions options = DatastoreServiceOptions.builder().dataset(DATASET).build();
  * DatastoreService datastore = DatastoreServiceFactory.getDefault(options);
- * KeyBuilder keyBuilder = datastore.newKeyBuilder(kind);
+ * KeyFactory keyBuilder = datastore.newKeyBuilder(kind);
  * Key key = keyBuilder.build(keyName);
  * Entity entity = datastore.get(key);
  * if (entity == null) {
diff --git a/src/main/java/com/google/gcloud/storage/Bucket.java b/src/main/java/com/google/gcloud/storage/Bucket.java
index fa7bae1da7fd..f0f9f8f4b52f 100644
--- a/src/main/java/com/google/gcloud/storage/Bucket.java
+++ b/src/main/java/com/google/gcloud/storage/Bucket.java
@@ -28,6 +28,7 @@ public interface Bucket {
   // that object can return its own meta-data, update its own meta-data, replace its content
   // via a stream or byteBuffer, read its content (via stream or ByteBuffer),...
   //void copy(String source, String bucket, String dest);
+  // Also consider read with an offset (and limit).
 
   void put(String name, ByteBuffer bytes);
 
diff --git a/src/test/java/com/google/gcloud/ExceptionHandlerTest.java b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java
index f88c5211ad93..10bd594b46a2 100644
--- a/src/test/java/com/google/gcloud/ExceptionHandlerTest.java
+++ b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java
@@ -4,19 +4,20 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import com.google.gcloud.ExceptionHandler.Interceptor;
+
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.nio.channels.ClosedByInterruptException;
 import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Tests for {@link ExceptionHandler}.
  */
-@RunWith(JUnit4.class)
 public class ExceptionHandlerTest {
 
   @Test
@@ -92,6 +93,7 @@ private static  void assertInvalidCallable(Callable callable, ExceptionHan
     }
   }
 
+  @SuppressWarnings("serial")
   @Test
   public void testShouldTry() {
     ExceptionHandler handler = ExceptionHandler.builder().retryOn(IOException.class).build();
@@ -99,15 +101,52 @@ public void testShouldTry() {
     assertTrue(handler.shouldRetry(new ClosedByInterruptException()));
     assertFalse(handler.shouldRetry(new RuntimeException()));
 
-    handler = ExceptionHandler.builder()
+    ExceptionHandler.Builder builder = ExceptionHandler.builder()
         .retryOn(IOException.class, NullPointerException.class)
         .abortOn(RuntimeException.class, ClosedByInterruptException.class,
-            InterruptedException.class)
-        .build();
+            InterruptedException.class);
+
+    handler = builder.build();
     assertTrue(handler.shouldRetry(new IOException()));
     assertFalse(handler.shouldRetry(new ClosedByInterruptException()));
     assertFalse(handler.shouldRetry(new InterruptedException()));
     assertFalse(handler.shouldRetry(new RuntimeException()));
     assertTrue(handler.shouldRetry(new NullPointerException()));
+
+    final AtomicReference before = new AtomicReference<>(false);
+
+    Interceptor interceptor = new Interceptor() {
+      @Override
+      public boolean shouldRetry(Exception exception, boolean shouldRetry) {
+        return !shouldRetry;
+      }
+
+      @Override
+      public Boolean shouldRetry(Exception exception) {
+        return before.get();
+      }
+    };
+
+    builder.interceptor(interceptor);
+    handler = builder.build();
+    assertFalse(handler.shouldRetry(new IOException()));
+    assertFalse(handler.shouldRetry(new ClosedByInterruptException()));
+    assertFalse(handler.shouldRetry(new InterruptedException()));
+    assertFalse(handler.shouldRetry(new RuntimeException()));
+    assertFalse(handler.shouldRetry(new NullPointerException()));
+
+    before.set(true);
+    assertTrue(handler.shouldRetry(new IOException()));
+    assertTrue(handler.shouldRetry(new ClosedByInterruptException()));
+    assertTrue(handler.shouldRetry(new InterruptedException()));
+    assertTrue(handler.shouldRetry(new RuntimeException()));
+    assertTrue(handler.shouldRetry(new NullPointerException()));
+
+    before.set(null);
+    assertFalse(handler.shouldRetry(new IOException()));
+    assertTrue(handler.shouldRetry(new ClosedByInterruptException()));
+    assertTrue(handler.shouldRetry(new InterruptedException()));
+    assertTrue(handler.shouldRetry(new RuntimeException()));
+    assertFalse(handler.shouldRetry(new NullPointerException()));
   }
 }
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
index fadd24147c67..92cd31cc282b 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
@@ -363,9 +363,9 @@ public void testRunStructuredQuery() {
 
   @Test
   public void testAllocateId() {
-    KeyBuilder keyBuilder = datastore.newKeyBuilder(KIND1);
-    PartialKey pk1 = keyBuilder.build();
-    Key key1 = keyBuilder.allocateIdAndBuild();
+    KeyFactory keyFactory = new KeyFactory(datastore, KIND1);
+    PartialKey pk1 = keyFactory.newKey();
+    Key key1 = keyFactory.allocateId();
     assertEquals(key1.dataset(), pk1.dataset());
     assertEquals(key1.namespace(), pk1.namespace());
     assertEquals(key1.ancestors(), pk1.ancestors());
@@ -385,11 +385,11 @@ public void testAllocateId() {
 
   @Test
   public void testAllocateIdArray() {
-    KeyBuilder keyBuilder = datastore.newKeyBuilder(KIND1);
-    PartialKey partialKey1 = keyBuilder.build();
-    PartialKey partialKey2 = keyBuilder.kind(KIND2).addAncestor(KIND1, 10).build();
-    Key key3 = keyBuilder.build("name");
-    Key key4 = keyBuilder.build(1);
+    KeyFactory keyFactory = new KeyFactory(datastore, KIND1);
+    PartialKey partialKey1 = keyFactory.newKey();
+    PartialKey partialKey2 = keyFactory.kind(KIND2).addAncestor(KIND1, 10).newKey();
+    Key key3 = keyFactory.newKey("name");
+    Key key4 = keyFactory.newKey(1);
     Iterator result =
         datastore.allocateId(partialKey1, partialKey2, key3, key4, partialKey1, key3);
     Map map = new HashMap<>();
@@ -525,12 +525,12 @@ public void testDelete() {
   }
 
   @Test
-  public void testNewKeyBuilder() {
-    KeyBuilder keyBuilder = datastore.newKeyBuilder(KIND1);
-    assertEquals(PARTIAL_KEY1, keyBuilder.build());
+  public void testKeyFactory() {
+    KeyFactory keyFactory = new KeyFactory(datastore, KIND1);
+    assertEquals(PARTIAL_KEY1, keyFactory.newKey());
     assertEquals(PartialKey.builder(PARTIAL_KEY1).kind(KIND2).build(),
-        datastore.newKeyBuilder(KIND2).build());
-    assertEquals(KEY1, keyBuilder.build("name"));
-    assertEquals(Key.builder(KEY1).id(2).build(), keyBuilder.build(2));
+        new KeyFactory(datastore, KIND2).newKey());
+    assertEquals(KEY1, keyFactory.newKey("name"));
+    assertEquals(Key.builder(KEY1).id(2).build(), keyFactory.newKey(2));
   }
 }

From 84e9d3219900e449d081345cb16cd0512aa97903 Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Wed, 17 Dec 2014 17:48:06 -0800
Subject: [PATCH 062/771] handle deferred results for gets and change gets
 semantics (unordered).

---
 .../google/gcloud/datastore/BaseEntity.java   |  4 +-
 .../google/gcloud/datastore/BatchWriter.java  | 29 +++++-
 .../gcloud/datastore/DatastoreReader.java     |  9 +-
 .../datastore/DatastoreServiceImpl.java       | 69 ++++++++------
 .../com/google/gcloud/datastore/GqlQuery.java |  4 +-
 .../google/gcloud/datastore/QueryResult.java  |  2 +
 .../gcloud/datastore/QueryResultImpl.java     |  2 +-
 .../gcloud/datastore/StructuredQuery.java     |  4 +-
 .../google/gcloud/datastore/Transaction.java  | 10 +-
 .../gcloud/datastore/TransactionImpl.java     |  7 +-
 .../google/gcloud/datastore/package-info.java |  4 +-
 .../datastore/DatastoreServiceTest.java       | 92 +++++++++++--------
 12 files changed, 148 insertions(+), 88 deletions(-)

diff --git a/src/main/java/com/google/gcloud/datastore/BaseEntity.java b/src/main/java/com/google/gcloud/datastore/BaseEntity.java
index d5497bb0b308..411967135b10 100644
--- a/src/main/java/com/google/gcloud/datastore/BaseEntity.java
+++ b/src/main/java/com/google/gcloud/datastore/BaseEntity.java
@@ -108,8 +108,8 @@ public B set(String name, List> values) {
       return self();
     }
 
-    public B set(String name, Value... value) {
-      properties.put(name, of(Arrays.asList(value)));
+    public B set(String name, Value value, Value... other) {
+      properties.put(name, of(value, other));
       return self();
     }
 
diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriter.java b/src/main/java/com/google/gcloud/datastore/BatchWriter.java
index 99aeafabd3d4..9fadd9eca6a9 100644
--- a/src/main/java/com/google/gcloud/datastore/BatchWriter.java
+++ b/src/main/java/com/google/gcloud/datastore/BatchWriter.java
@@ -20,22 +20,45 @@ public interface BatchWriter extends DatastoreWriter {
 
   /**
    * {@inheritDoc}
-   * @throws DatastoreServiceException if a given entity already added to this batch
+   * This operation will be converted to {@link #put} operation for entities that were already
+   *     marked for deletion in this batch.
+   * @throws DatastoreServiceException if a given entity already added to this batch or if batch
+   *     is no longer active
    */
   @Override
   void add(Entity... entity);
 
   /**
    * {@inheritDoc}
-   * @throws DatastoreServiceException if an entity is marked for deletion in this batch
+   * This operation will be converted to {@link #put} operation for entities that were already
+   *     added or put in this batch.
+   * @throws DatastoreServiceException if an entity is marked for deletion in this batch or if
+   *     batch is no longer active
    */
   @Override
   void update(Entity... entity);
 
+  /**
+   * {@inheritDoc}
+   * This operation will also remove from this batch any prior writes for entities with the same
+   *     keys.
+   * @throws DatastoreServiceException if batch is no longer active
+   */
+  @Override
+  public void delete(Key... key);
+
+  /**
+   * {@inheritDoc}
+   * This operation will also remove from this batch any prior writes for the same entities.
+   * @throws DatastoreServiceException if batch is no longer active
+   */
+  @Override
+  public void put(Entity... entity);
+
   /**
    * Submit the batch to the Datastore.
    *
-   * @throws DatastoreServiceException if there was any failure.
+   * @throws DatastoreServiceException if there was any failure or if batch is not longer active
    */
   void submit();
 
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java
index ec7052e414e0..9fc1f0c5b9b5 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java
@@ -15,8 +15,11 @@ public interface DatastoreReader {
   Entity get(Key key);
 
   /**
-   * Returns an {@link Entity} for each given {@link Key} or {@code null} if does not exists
-   * ordered by input.
+   * Returns an {@link Entity} for each given {@link Key} that exists in the Datastore.
+   * The order of the result is unspecified.
+   * Results are loaded lazily therefore it is possible to get a DatastoreServiceException
+   * from the returned {@code Iterator} {@link Iterator#hasNext hasNext} or
+   * {@link Iterator#next next} calls.
    *
    * @throws DatastoreServiceException upon failure.
    */
@@ -27,5 +30,5 @@ public interface DatastoreReader {
    *
    * @throws DatastoreServiceException upon failure.
    */
-   QueryResult runQuery(Query query);
+   QueryResult run(Query query);
 }
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
index 7af34773c3b8..7d7f7c8191f6 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
@@ -7,11 +7,9 @@
 import com.google.protobuf.ByteString;
 
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
-import java.util.Map;
 
 
 final class DatastoreServiceImpl implements DatastoreService {
@@ -43,15 +41,15 @@ public Transaction newTransaction(TransactionOption... transactionOption) {
   }
 
   @Override
-  public  QueryResult runQuery(Query query) {
-    return runQuery(null, query);
+  public  QueryResult run(Query query) {
+    return run(null, query);
   }
 
-   QueryResult runQuery(DatastoreV1.ReadOptions readOptionsPb, Query query) {
+   QueryResult run(DatastoreV1.ReadOptions readOptionsPb, Query query) {
     return new QueryResultImpl<>(this, readOptionsPb, query);
   }
 
-  DatastoreV1.RunQueryResponse runQuery(DatastoreV1.RunQueryRequest requestPb) {
+  DatastoreV1.RunQueryResponse run(DatastoreV1.RunQueryRequest requestPb) {
     try {
       return datastore.runQuery(requestPb);
     } catch (DatastoreException e) {
@@ -99,7 +97,8 @@ private PartialKey trimNameOrId(PartialKey key) {
 
   @Override
   public Entity get(Key key) {
-    return get(key, EMPTY_KEY_ARRAY).next();
+    Iterator iter = get(key, EMPTY_KEY_ARRAY);
+    return iter.hasNext() ? iter.next() : null;
   }
 
   @Override
@@ -108,7 +107,7 @@ public Iterator get(Key key, Key... others) {
   }
 
   Iterator get(DatastoreV1.ReadOptions readOptionsPb, final Key key, final Key... others) {
-    DatastoreV1.LookupRequest.Builder requestPb = DatastoreV1.LookupRequest.newBuilder();
+    final DatastoreV1.LookupRequest.Builder requestPb = DatastoreV1.LookupRequest.newBuilder();
     if (readOptionsPb != null) {
       requestPb.setReadOptions(readOptionsPb);
     }
@@ -118,30 +117,44 @@ Iterator get(DatastoreV1.ReadOptions readOptionsPb, final Key key, final
     for (Key k : dedupKeys) {
       requestPb.addKey(k.toPb());
     }
-    try {
-      DatastoreV1.LookupResponse responsePb = datastore.lookup(requestPb.build());
-      final Map result = new HashMap<>();
-      for (DatastoreV1.EntityResult entityResultPb : responsePb.getFoundList()) {
-        Entity entity = Entity.fromPb(entityResultPb.getEntity());
-        result.put(entity.key(), entity);
+    return new ResultsIterator(requestPb);
+  }
+
+  final class ResultsIterator extends AbstractIterator {
+
+    private final DatastoreV1.LookupRequest.Builder requestPb;
+    Iterator iter;
+
+    ResultsIterator(DatastoreV1.LookupRequest.Builder requestPb) {
+      this.requestPb = requestPb;
+      loadResults();
+    }
+
+    private void loadResults() {
+      try {
+        DatastoreV1.LookupResponse responsePb = datastore.lookup(requestPb.build());
+        iter = responsePb.getFoundList().iterator();
+        requestPb.clearKey();
+        if (responsePb.getDeferredCount() > 0) {
+          requestPb.addAllKey(responsePb.getDeferredList());
+        }
+      } catch (DatastoreException e) {
+        throw DatastoreServiceException.translateAndThrow(e);
       }
-      return new AbstractIterator() {
-        int index = -2;
+    }
 
-        @Override
-        protected Entity computeNext() {
-          ++index;
-          if (index < 0) {
-            return result.get(key);
-          }
-          if (index < others.length) {
-            return result.get(others[index]);
-          }
+    @Override
+    protected Entity computeNext() {
+      if (iter.hasNext()) {
+        return Entity.fromPb(iter.next().getEntity());
+      }
+      while (!iter.hasNext()) {
+        if (requestPb.getKeyCount() == 0) {
           return endOfData();
         }
-      };
-    } catch (DatastoreException e) {
-      throw DatastoreServiceException.translateAndThrow(e);
+        loadResults();
+      }
+      return Entity.fromPb(iter.next().getEntity());
     }
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
index f8769d8ac0ae..df24b787b3fb 100644
--- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
@@ -29,7 +29,7 @@
  * 

When the type of the results is known the preferred usage would be: *

 {@code
  *   Query query = GqlQuery.builder(ResultClass.full(), "select * from kind").build();
- *   QueryResult results = datastore.runQuery(query);
+ *   QueryResult results = datastore.run(query);
  *   while (results.hasNext()) {
  *     Entity entity = results.next();
  *     ...
@@ -39,7 +39,7 @@
  * 

When the type of the results is unknown you can use this approach: *

 {@code
  *   Query query = GqlQuery.builder("select __key__ from kind").build();
- *   QueryResult results = datastore.runQuery(query);
+ *   QueryResult results = datastore.run(query);
  *   if (Key.class.isAssignableFrom(results.resultClass())) {
  *     QueryResult keys = (QueryResult) results;
  *     while (keys.hasNext()) {
diff --git a/src/main/java/com/google/gcloud/datastore/QueryResult.java b/src/main/java/com/google/gcloud/datastore/QueryResult.java
index 4c69f6b73eaa..aa615b755f19 100644
--- a/src/main/java/com/google/gcloud/datastore/QueryResult.java
+++ b/src/main/java/com/google/gcloud/datastore/QueryResult.java
@@ -6,6 +6,8 @@
  * The result of a Google Cloud Datastore query submission.
  * When result is not typed it is possible to cast it to its appropriate type according to
  * the {@link #resultClass} value.
+ * Results are loaded lazily therefore it is possible to get a DatastoreServiceException
+ * upon {@link #hasNext} or {@link #next} calls.
  *
  * @param V the type of the results value.
  */
diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
index eecf6a196e6c..ef18701bf4c1 100644
--- a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
@@ -55,7 +55,7 @@ private DatastoreV1.QueryResultBatch sendRequest() {
     }
     requestPb.setPartitionId(partitionIdPb);
     query.populatePb(requestPb);
-    resultPb = datastore.runQuery(requestPb.build()).getBatch();
+    resultPb = datastore.run(requestPb.build()).getBatch();
     entityResultPbIter = resultPb.getEntityResultList().iterator();
     // cursor = resultPb.getSkippedCursor(); // only available in v1beta3
     type = RESULT_TYPE_CONVERTER.get(resultPb.getEntityResultType());
diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index f9a733d1a529..328d8a896d8c 100644
--- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -31,7 +31,7 @@
  * 

A simple query that returns all entities for a specific kind *

 {@code
  *   StructuredQuery query = StructuredQuery.builder().kind(kind).build();
- *   QueryResult results = datastore.runQuery(query);
+ *   QueryResult results = datastore.run(query);
  *   while (results.hasNext()) {
  *     Entity entity = results.next();
  *     ...
@@ -49,7 +49,7 @@
  *       .orderBy(OrderBy.asc("age"))
  *       .limit(10)
  *       .build();
- *   QueryResult results = datastore.runQuery(query);
+ *   QueryResult results = datastore.run(query);
  *   ...
  * } 
* diff --git a/src/main/java/com/google/gcloud/datastore/Transaction.java b/src/main/java/com/google/gcloud/datastore/Transaction.java index 2a9267cf6d41..06c4e5a45997 100644 --- a/src/main/java/com/google/gcloud/datastore/Transaction.java +++ b/src/main/java/com/google/gcloud/datastore/Transaction.java @@ -41,7 +41,7 @@ public interface Transaction extends DatastoreReaderWriter { * to fail if entity was changed by others after it was seen by this transaction) but any * write changes in this transaction will not be reflected by the returned entity. * - * @throws DatastoreServiceException upon failure. + * @throws DatastoreServiceException upon failure or if no longer active */ @Override Entity get(Key key); @@ -52,7 +52,7 @@ public interface Transaction extends DatastoreReaderWriter { * to fail if any of the entities was changed by others after they were seen by this transaction) * but any write changes in this transaction will not be reflected by the returned entities. * - * @throws DatastoreServiceException upon failure. + * @throws DatastoreServiceException upon failure or if no longer active */ @Override Iterator get(Key key, Key... others); @@ -64,15 +64,15 @@ public interface Transaction extends DatastoreReaderWriter { * query was performed) but any write changes in this transaction will not be reflected by * the result. * - * @throws DatastoreServiceException upon failure. + * @throws DatastoreServiceException upon failure or if no longer active */ @Override - QueryResult runQuery(Query query); + QueryResult run(Query query); /** * Commit the transaction. * - * @throws DatastoreServiceException if could not commit the transaction + * @throws DatastoreServiceException if could not commit the transaction or if no longer active */ void commit(); diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java index ee0dc31685ed..a2e15b311689 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java @@ -29,7 +29,8 @@ public final class TransactionImpl extends BatchWriterImpl implements Transactio @Override public Entity get(Key key) { - return get(key, DatastoreServiceImpl.EMPTY_KEY_ARRAY).next(); + Iterator iter = get(key, DatastoreServiceImpl.EMPTY_KEY_ARRAY); + return iter.hasNext() ? iter.next() : null; } @Override @@ -41,11 +42,11 @@ public Iterator get(Key key, Key... others) { } @Override - public QueryResult runQuery(Query query) { + public QueryResult run(Query query) { checkActive(); DatastoreV1.ReadOptions.Builder readOptionsPb = DatastoreV1.ReadOptions.newBuilder(); readOptionsPb.setTransaction(transaction); - return datastore.runQuery(readOptionsPb.build(), query); + return datastore.run(readOptionsPb.build(), query); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/package-info.java b/src/main/java/com/google/gcloud/datastore/package-info.java index 3babfd370e11..ea3e92b7386b 100644 --- a/src/main/java/com/google/gcloud/datastore/package-info.java +++ b/src/main/java/com/google/gcloud/datastore/package-info.java @@ -5,8 +5,8 @@ *
 {@code
  * DatastoreServiceOptions options = DatastoreServiceOptions.builder().dataset(DATASET).build();
  * DatastoreService datastore = DatastoreServiceFactory.getDefault(options);
- * KeyFactory keyBuilder = datastore.newKeyBuilder(kind);
- * Key key = keyBuilder.build(keyName);
+ * KeyFactory keyFactory = new KeyFactory(datastore, kind);
+ * Key key = keyFactory.newKey(keyName);
  * Entity entity = datastore.get(key);
  * if (entity == null) {
  *   entity = Entity.builder(key)
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
index 92cd31cc282b..a7c6626d6b92 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
@@ -8,6 +8,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import com.google.common.collect.Maps;
 import com.google.gcloud.datastore.Query.ResultClass;
 import com.google.gcloud.datastore.StructuredQuery.OrderBy;
 import com.google.gcloud.datastore.StructuredQuery.Projection;
@@ -16,9 +17,11 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 
 public class DatastoreServiceTest {
@@ -93,11 +96,11 @@ public void testNewTransactionCommit() {
     transaction.delete(KEY1);
     transaction.commit();
 
-    Iterator iter = datastore.get(KEY1, KEY2, KEY3);
-    assertNull(iter.next());
-    assertEquals(entity2, iter.next());
-    assertEquals(ENTITY3, iter.next());
-    assertFalse(iter.hasNext());
+    List list = fetch(KEY1, KEY2, KEY3);
+    assertNull(list.get(0));
+    assertEquals(entity2, list.get(1));
+    assertEquals(ENTITY3, list.get(2));
+    assertEquals(3, list.size());
 
     try {
       transaction.commit();
@@ -143,7 +146,7 @@ public void testTransactionWithQuery() {
     Query query =
         StructuredQuery.builder().kind(KIND2).filter(PropertyFilter.hasAncestor(KEY2)).build();
     Transaction transaction = datastore.newTransaction();
-    QueryResult results = transaction.runQuery(query);
+    QueryResult results = transaction.run(query);
     assertEquals(ENTITY2, results.next());
     assertFalse(results.hasNext());
     transaction.add(ENTITY3);
@@ -151,7 +154,7 @@ public void testTransactionWithQuery() {
     assertEquals(ENTITY3, datastore.get(KEY3));
 
     transaction = datastore.newTransaction();
-    results = transaction.runQuery(query);
+    results = transaction.run(query);
     assertEquals(ENTITY2, results.next());
     transaction.delete(ENTITY3.key());
     // update entity2 during the transaction
@@ -185,11 +188,11 @@ public void testNewTransactionRollback() {
 
     verifyNotUsable(transaction);
 
-    Iterator iter = datastore.get(KEY1, KEY2, KEY3);
-    assertEquals(ENTITY1, iter.next());
-    assertEquals(ENTITY2, iter.next());
-    assertNull(iter.next());
-    assertFalse(iter.hasNext());
+    List list = fetch(KEY1, KEY2, KEY3);
+    assertEquals(ENTITY1, list.get(0));
+    assertEquals(ENTITY2, list.get(1));
+    assertNull(list.get(2));
+    assertEquals(3, list.size());
   }
 
   private void verifyNotUsable(DatastoreWriter writer) {
@@ -232,7 +235,7 @@ public void testNewBatchWriter() {
     batchWriter.add(entity4, entity5);
     batchWriter.put(ENTITY3, entity1, entity2);
     batchWriter.submit();
-    Iterator entities = datastore.get(KEY1, KEY2, KEY3, entity4.key(), entity5.key());
+    Iterator entities = fetch(KEY1, KEY2, KEY3, entity4.key(), entity5.key()).iterator();
     assertEquals(entity1, entities.next());
     assertEquals(entity2, entities.next());
     assertEquals(ENTITY3, entities.next());
@@ -252,7 +255,7 @@ public void testNewBatchWriter() {
     batchWriter.delete(entity4.key(), entity5.key());
     batchWriter.update(ENTITY1, ENTITY2, ENTITY3);
     batchWriter.submit();
-    entities = datastore.get(KEY1, KEY2, KEY3, entity4.key(), entity5.key());
+    entities = fetch(KEY1, KEY2, KEY3, entity4.key(), entity5.key()).iterator();
     assertEquals(ENTITY1, entities.next());
     assertEquals(ENTITY2, entities.next());
     assertEquals(ENTITY3, entities.next());
@@ -270,7 +273,7 @@ public void testNewBatchWriter() {
   @Test
   public void testRunGqlQueryNoCasting() {
     Query query1 = GqlQuery.builder(ResultClass.full(), "select * from " + KIND1).build();
-    QueryResult results1 = datastore.runQuery(query1);
+    QueryResult results1 = datastore.run(query1);
     assertTrue(results1.hasNext());
     assertEquals(ENTITY1, results1.next());
     assertFalse(results1.hasNext());
@@ -278,7 +281,7 @@ public void testRunGqlQueryNoCasting() {
     datastore.put(ENTITY3);
     Query query2 =  GqlQuery.builder(
         ResultClass.full(), "select * from " + KIND2 + " order by __key__").build();
-    QueryResult results2 = datastore.runQuery(query2);
+    QueryResult results2 = datastore.run(query2);
     assertTrue(results2.hasNext());
     assertEquals(ENTITY2, results2.next());
     assertTrue(results2.hasNext());
@@ -286,19 +289,19 @@ public void testRunGqlQueryNoCasting() {
     assertFalse(results2.hasNext());
 
     query1 = GqlQuery.builder(ResultClass.full(), "select * from bla").build();
-    results1 = datastore.runQuery(query1);
+    results1 = datastore.run(query1);
     assertFalse(results1.hasNext());
 
     Query keyOnlyQuery =
         GqlQuery.builder(ResultClass.keyOnly(), "select __key__ from " + KIND1).build();
-    QueryResult keyOnlyResults = datastore.runQuery(keyOnlyQuery);
+    QueryResult keyOnlyResults = datastore.run(keyOnlyQuery);
     assertTrue(keyOnlyResults.hasNext());
     assertEquals(KEY1, keyOnlyResults.next());
     assertFalse(keyOnlyResults.hasNext());
 
     Query projectionQuery = GqlQuery.builder(
         ResultClass.projection(), "select str from " + KIND1).build();
-    QueryResult projectionResult = datastore.runQuery(projectionQuery);
+    QueryResult projectionResult = datastore.run(projectionQuery);
     assertTrue(projectionResult.hasNext());
     PartialEntity partialEntity = projectionResult.next();
     assertEquals("str", partialEntity.getString("str"));
@@ -310,13 +313,13 @@ public void testRunGqlQueryNoCasting() {
   public void testRunGqlQueryWithCasting() {
     @SuppressWarnings("unchecked")
     Query query1 = (Query) GqlQuery.builder("select * from " + KIND1).build();
-    QueryResult results1 = datastore.runQuery(query1);
+    QueryResult results1 = datastore.run(query1);
     assertTrue(results1.hasNext());
     assertEquals(ENTITY1, results1.next());
     assertFalse(results1.hasNext());
 
     Query query2 = GqlQuery.builder("select * from " + KIND1).build();
-    QueryResult results2 = datastore.runQuery(query2);
+    QueryResult results2 = datastore.run(query2);
     assertEquals(Entity.class, results2.resultClass());
     @SuppressWarnings("unchecked")
     QueryResult results3 = (QueryResult) results2;
@@ -329,13 +332,13 @@ public void testRunGqlQueryWithCasting() {
   public void testRunStructuredQuery() {
     StructuredQuery query =
         StructuredQuery.builder().kind(KIND1).orderBy(OrderBy.asc("__key__")).build();
-    QueryResult results1 = datastore.runQuery(query);
+    QueryResult results1 = datastore.run(query);
     assertTrue(results1.hasNext());
     assertEquals(ENTITY1, results1.next());
     assertFalse(results1.hasNext());
 
     StructuredQuery keyOnlyQuery =  StructuredQuery.keyOnlyBuilder().kind(KIND1).build();
-    QueryResult results2 = datastore.runQuery(keyOnlyQuery);
+    QueryResult results2 = datastore.run(keyOnlyQuery);
     assertTrue(results2.hasNext());
     assertEquals(ENTITY1.key(), results2.next());
     assertFalse(results2.hasNext());
@@ -349,7 +352,7 @@ public void testRunStructuredQuery() {
         .limit(10)
         .build();
 
-    QueryResult results3 = datastore.runQuery(projectionQuery);
+    QueryResult results3 = datastore.run(projectionQuery);
     assertTrue(results3.hasNext());
     PartialEntity entity = results3.next();
     assertEquals(ENTITY2.key(), entity.key());
@@ -428,7 +431,7 @@ public void testGet() {
   public void testGetArray() {
     datastore.put(ENTITY3);
     Iterator result =
-        datastore.get(KEY1, Key.builder(KEY1).name("bla").build(), KEY2, KEY3);
+        fetch(KEY1, Key.builder(KEY1).name("bla").build(), KEY2, KEY3).iterator();
     assertEquals(ENTITY1, result.next());
     assertNull(result.next());
     assertEquals(ENTITY2, result.next());
@@ -453,12 +456,27 @@ public void testGetArray() {
     assertFalse(result.hasNext());
   }
 
+  public List fetch(Key key, Key... others) {
+    Iterator entities = datastore.get(key, others);
+    Map map = Maps.newHashMapWithExpectedSize(1 + others.length);
+    while (entities.hasNext()) {
+      Entity entity = entities.next();
+      map.put(entity.key(), entity);
+    }
+    List list = new ArrayList<>(1 + others.length);
+    list.add(map.get(key));
+    for (Key other : others) {
+      list.add(map.get(other));
+    }
+    return list;
+  }
+
   @Test
   public void testAdd() {
-    Iterator keys = datastore.get(ENTITY1.key(), ENTITY3.key());
-    assertEquals(ENTITY1, keys.next());
-    assertNull(keys.next());
-    assertFalse(keys.hasNext());
+    List keys = fetch(ENTITY1.key(), ENTITY3.key());
+    assertEquals(ENTITY1, keys.get(0));
+    assertNull(keys.get(1));
+    assertEquals(2, keys.size());
 
     try {
       datastore.add(ENTITY1);
@@ -472,10 +490,10 @@ public void testAdd() {
 
   @Test
   public void testUpdate() {
-    Iterator keys = datastore.get(ENTITY1.key(), ENTITY3.key());
-    assertEquals(ENTITY1, keys.next());
-    assertNull(keys.next());
-    assertFalse(keys.hasNext());
+    List keys = fetch(ENTITY1.key(), ENTITY3.key());
+    assertEquals(ENTITY1, keys.get(0));
+    assertNull(keys.get(1));
+    assertEquals(2, keys.size());
 
     try {
       datastore.update(ENTITY3);
@@ -493,7 +511,7 @@ public void testUpdate() {
 
   @Test
   public void testPut() {
-    Iterator keys = datastore.get(ENTITY1.key(), ENTITY2.key(), ENTITY3.key());
+    Iterator keys = fetch(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()).iterator();
     assertEquals(ENTITY1, keys.next());
     assertEquals(ENTITY2, keys.next());
     assertNull(keys.next());
@@ -502,7 +520,7 @@ public void testPut() {
     Entity entity2 = Entity.builder(ENTITY2).clear().set("bla", new NullValue()).build();
     assertNotEquals(ENTITY2, entity2);
     datastore.put(ENTITY3, ENTITY1, entity2);
-    keys = datastore.get(ENTITY1.key(), ENTITY2.key(), ENTITY3.key());
+    keys = fetch(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()).iterator();
     assertEquals(ENTITY1, keys.next());
     assertEquals(entity2, keys.next());
     assertEquals(ENTITY3, keys.next());
@@ -511,13 +529,13 @@ public void testPut() {
 
   @Test
   public void testDelete() {
-    Iterator keys = datastore.get(ENTITY1.key(), ENTITY2.key(), ENTITY3.key());
+    Iterator keys = fetch(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()).iterator();
     assertEquals(ENTITY1, keys.next());
     assertEquals(ENTITY2, keys.next());
     assertNull(keys.next());
     assertFalse(keys.hasNext());
     datastore.delete(ENTITY1.key(), ENTITY2.key(), ENTITY3.key());
-    keys = datastore.get(ENTITY1.key(), ENTITY2.key(), ENTITY3.key());
+    keys = fetch(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()).iterator();
     assertNull(keys.next());
     assertNull(keys.next());
     assertNull(keys.next());

From d997837f3fdfe2611e023d906578bcc369e1d1cc Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Thu, 18 Dec 2014 10:03:56 -0800
Subject: [PATCH 063/771] minor change

---
 src/main/java/com/google/gcloud/datastore/BaseEntity.java       | 1 -
 .../java/com/google/gcloud/datastore/DatastoreServiceImpl.java  | 2 +-
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/main/java/com/google/gcloud/datastore/BaseEntity.java b/src/main/java/com/google/gcloud/datastore/BaseEntity.java
index 411967135b10..0ec36a2f0211 100644
--- a/src/main/java/com/google/gcloud/datastore/BaseEntity.java
+++ b/src/main/java/com/google/gcloud/datastore/BaseEntity.java
@@ -15,7 +15,6 @@
 import com.google.common.collect.ImmutableSortedMap;
 import com.google.gcloud.datastore.Value.Type;
 
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
index 7d7f7c8191f6..91bfa0137b57 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
@@ -107,7 +107,7 @@ public Iterator get(Key key, Key... others) {
   }
 
   Iterator get(DatastoreV1.ReadOptions readOptionsPb, final Key key, final Key... others) {
-    final DatastoreV1.LookupRequest.Builder requestPb = DatastoreV1.LookupRequest.newBuilder();
+    DatastoreV1.LookupRequest.Builder requestPb = DatastoreV1.LookupRequest.newBuilder();
     if (readOptionsPb != null) {
       requestPb.setReadOptions(readOptionsPb);
     }

From 9048e0b8f6fc36bcec9af2387af1755a593b320e Mon Sep 17 00:00:00 2001
From: aozarov 
Date: Thu, 18 Dec 2014 17:03:46 -0800
Subject: [PATCH 064/771] change projection query to return projection entity

---
 .../gcloud/datastore/BatchWriteOption.java    |   3 +-
 .../gcloud/datastore/DatastoreReader.java     |   6 +-
 .../com/google/gcloud/datastore/GqlQuery.java |  20 +-
 .../gcloud/datastore/PartialEntity.java       |   2 +-
 .../gcloud/datastore/ProjectionEntity.java    | 128 +++++++++++++
 .../com/google/gcloud/datastore/Query.java    | 177 +++++++++---------
 .../google/gcloud/datastore/QueryResult.java  |   4 +-
 .../gcloud/datastore/QueryResultImpl.java     |  36 ++--
 .../gcloud/datastore/StructuredQuery.java     |  39 ++--
 .../gcloud/datastore/TransactionImpl.java     |  19 +-
 .../gcloud/datastore/TransactionOption.java   |  44 ++++-
 .../datastore/DatastoreServiceTest.java       |  59 +++---
 .../gcloud/datastore/SerializationTest.java   |  10 +-
 13 files changed, 364 insertions(+), 183 deletions(-)
 create mode 100644 src/main/java/com/google/gcloud/datastore/ProjectionEntity.java

diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java b/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java
index 2fea6e95d893..f2bf33266252 100644
--- a/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java
+++ b/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java
@@ -2,10 +2,9 @@
 
 import com.google.common.collect.ImmutableMap;
 
-import java.io.Serializable;
 import java.util.Map;
 
-public abstract class BatchWriteOption implements Serializable {
+public abstract class BatchWriteOption implements java.io.Serializable {
 
   private static final long serialVersionUID = -3932758377282659839L;
 
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java
index 9fc1f0c5b9b5..ea33c0104388 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java
@@ -17,9 +17,9 @@ public interface DatastoreReader {
   /**
    * Returns an {@link Entity} for each given {@link Key} that exists in the Datastore.
    * The order of the result is unspecified.
-   * Results are loaded lazily therefore it is possible to get a DatastoreServiceException
-   * from the returned {@code Iterator} {@link Iterator#hasNext hasNext} or
-   * {@link Iterator#next next} calls.
+   * Results are loaded lazily therefore it is possible to get a {@code DatastoreServiceException}
+   * from the returned {@code Iterator}'s {@link Iterator#hasNext hasNext} or
+   * {@link Iterator#next next} methods.
    *
    * @throws DatastoreServiceException upon failure.
    */
diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
index df24b787b3fb..c80cb4a77f14 100644
--- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
@@ -28,7 +28,7 @@
  *
  * 

When the type of the results is known the preferred usage would be: *

 {@code
- *   Query query = GqlQuery.builder(ResultClass.full(), "select * from kind").build();
+ *   Query query = GqlQuery.builder(Query.Type.FULL, "select * from kind").build();
  *   QueryResult results = datastore.run(query);
  *   while (results.hasNext()) {
  *     Entity entity = results.next();
@@ -142,15 +142,15 @@ static Argument fromPb(DatastoreV1.GqlQueryArg argPb) {
    */
   public static final class Builder {
 
-    private final ResultClass resultClass;
+    private final Type type;
     private String namespace;
     private String queryString;
     private boolean allowLiteral;
     private Map nameArgs = new TreeMap<>();
     private List numberArgs = new LinkedList<>();
 
-    Builder(ResultClass resultClass, String query) {
-      this.resultClass = checkNotNull(resultClass);
+    Builder(Type type, String query) {
+      this.type = checkNotNull(type);
       queryString = checkNotNull(query);
     }
 
@@ -294,7 +294,7 @@ private static Argument toArgument(String name, Value.BuilderFactory builderFact
   }
 
   private GqlQuery(Builder builder) {
-    super(builder.resultClass, builder.namespace);
+    super(builder.type, builder.namespace);
     queryString = builder.queryString;
     allowLiteral = builder.allowLiteral;
     nameArgs = ImmutableList.copyOf(builder.nameArgs.values());
@@ -378,12 +378,12 @@ protected GqlQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) {
   }
 
   @Override
-  protected Object fromPb(ResultClass resultType, String namespace, byte[] bytesPb)
+  protected Object fromPb(Type resultType, String namespace, byte[] bytesPb)
       throws InvalidProtocolBufferException {
     return fromPb(resultType, namespace, DatastoreV1.GqlQuery.parseFrom(bytesPb));
   }
 
-  static  GqlQuery fromPb(ResultClass resultType, String namespace,
+  static  GqlQuery fromPb(Type resultType, String namespace,
       DatastoreV1.GqlQuery queryPb) {
     Builder builder = new Builder<>(resultType, queryPb.getQueryString());
     builder.namespace(namespace);
@@ -407,7 +407,7 @@ static  GqlQuery fromPb(ResultClass resultType, String namespace,
    * @see GQL Reference
    */
   public static GqlQuery.Builder builder(String gql) {
-    return builder(ResultClass.unknown(), gql);
+    return builder(Type.UNKNOWN, gql);
   }
 
   /**
@@ -415,7 +415,7 @@ public static GqlQuery.Builder builder(String gql) {
    *
    * @see GQL Reference
    */
-  public static  GqlQuery.Builder builder(ResultClass resultClass, String gql) {
-    return new GqlQuery.Builder<>(resultClass, gql);
+  public static  GqlQuery.Builder builder(Type type, String gql) {
+    return new GqlQuery.Builder<>(type, gql);
   }
 }
diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java
index 9edefac1ca34..df520d3f48b9 100644
--- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java
+++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java
@@ -48,7 +48,7 @@ protected PartialEntity(PartialKey key, ImmutableSortedMap> pro
   }
 
   /**
-   * Returns a new {@link #Entity} with the same properties as this one and
+   * Returns a new {@link Entity} with the same properties as this one and
    * with the given {@code key}.
    */
   public Entity toEntity(Key key) {
diff --git a/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java b/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java
new file mode 100644
index 000000000000..6b219ce0968c
--- /dev/null
+++ b/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java
@@ -0,0 +1,128 @@
+package com.google.gcloud.datastore;
+
+import com.google.api.services.datastore.DatastoreV1;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import java.util.Objects;
+
+/**
+ * A projection entity is a result of a Google Cloud Datastore projection query.
+ * A projection entity holds one or more properties, represented by a name (as {@link String})
+ * and a value (as {@link Value}), and may have a {@link Key}.
+ *
+ * @see Google Cloud Datastore projection queries
+ * @see Google Cloud Datastore Entities, Properties, and Keys
+ */
+public final class ProjectionEntity extends BaseEntity {
+
+  private static final long serialVersionUID = 432961565733066915L;
+
+  private final Key key;
+
+  static final class Builder extends BaseEntity.Builder {
+
+    private Key key;
+
+    private Builder() {
+    }
+
+    private Builder(ProjectionEntity entity) {
+      super(entity);
+      key = entity.key();
+    }
+
+    public Builder key(Key key) {
+      this.key = key;
+      return this;
+    }
+
+    @Override
+    public ProjectionEntity build() {
+      return new ProjectionEntity(key, ImmutableSortedMap.copyOf(properties));
+    }
+  }
+
+  ProjectionEntity(Key key, ImmutableSortedMap> properties) {
+    super(properties);
+    this.key = key;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(key, properties());
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    }
+    if (!(obj instanceof ProjectionEntity)) {
+      return false;
+    }
+    ProjectionEntity other = (ProjectionEntity) obj;
+    return Objects.equals(key, other.key)
+        && Objects.equals(properties(), other.properties());
+  }
+
+  public boolean hasKey() {
+    return key() != null;
+  }
+
+  /**
+   * Returns the associated {@link Key} or null if it does not have one.
+   */
+  public Key key() {
+    return key;
+  }
+
+  @Override
+  public DateTime getDateTime(String name) {
+    Value value = getValue(name);
+    if (value.hasMeaning() && value.meaning() == 18 && value instanceof LongValue) {
+      return new DateTime(getLong(name));
+    }
+    return ((DateTimeValue) value).get();
+  }
+
+  @Override
+  public Blob getBlob(String name) {
+    Value value = getValue(name);
+    if (value.hasMeaning() && value.meaning() == 18 && value instanceof StringValue) {
+      return new Blob(ByteString.copyFromUtf8(getString(name)), false);
+    }
+    return ((BlobValue) value).get();
+  }
+
+  @Override
+  protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException {
+    return fromPb(DatastoreV1.Entity.parseFrom(bytesPb));
+  }
+
+  static ProjectionEntity fromPb(DatastoreV1.Entity entityPb) {
+    ImmutableSortedMap.Builder> properties =
+        ImmutableSortedMap.naturalOrder();
+    for (DatastoreV1.Property property : entityPb.getPropertyList()) {
+      properties.put(property.getName(), Value.fromPb(property.getValue()));
+    }
+    Key key = null;
+    if (entityPb.hasKey()) {
+      key = Key.fromPb(entityPb.getKey());
+    }
+    return new ProjectionEntity(key, properties.build());
+  }
+
+
+  public static Builder builder(ProjectionEntity copyFrom) {
+    return new Builder(copyFrom);
+  }
+
+  @Override
+  protected void populateEntityBuilder(DatastoreV1.Entity.Builder entityPb) {
+    if (key != null) {
+      entityPb.setKey(key.toPb());
+    }
+  }
+}
diff --git a/src/main/java/com/google/gcloud/datastore/Query.java b/src/main/java/com/google/gcloud/datastore/Query.java
index 03df75c00312..c68e3246f62f 100644
--- a/src/main/java/com/google/gcloud/datastore/Query.java
+++ b/src/main/java/com/google/gcloud/datastore/Query.java
@@ -5,9 +5,12 @@
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.Maps;
 import com.google.protobuf.GeneratedMessage;
 import com.google.protobuf.InvalidProtocolBufferException;
 
+import java.util.EnumMap;
+
 
 /**
  * A Google Cloud Datastore query.
@@ -20,31 +23,84 @@ public abstract class Query extends Serializable {
 
   private static final long serialVersionUID = -2748141759901313101L;
 
-  private final ResultClass resultClass;
+  private final Type type;
   private final String namespace;
 
-  public static class ResultClass implements java.io.Serializable {
+  /**
+   * This class represents the expected type of the result.
+   *   FULL: A complete {@link Entity}.
+   *   PROJECTION: A partial entity, represented by {@link PartialEntity}.
+   *   KEY_ONLY: An entity's {@link Key}.
+   */
+  public abstract static class Type implements java.io.Serializable {
 
     private static final long serialVersionUID = 2104157695425806623L;
-    private static final ResultClass UNKNOWN = new ResultClass<>(Object.class);
-    private static final ResultClass FULL = new ResultClass<>(Entity.class);
-    private static final ResultClass KEY_ONLY = new ResultClass<>(Key.class);
-    private static final ResultClass PROJECTION =
-        new ResultClass<>(PartialEntity.class);
+    private static final EnumMap>
+        PB_TO_INSTANCE = Maps.newEnumMap(DatastoreV1.EntityResult.ResultType.class);
+
+    static final Type UNKNOWN = new Type(null, Object.class) {
+
+      private static final long serialVersionUID = 1602329532153860907L;
+
+      @Override protected Object convert(DatastoreV1.Entity entityPb) {
+        if (entityPb.getPropertyCount() == 0) {
+          if (!entityPb.hasKey()) {
+            return null;
+          }
+          return Key.fromPb(entityPb.getKey());
+        }
+        return ProjectionEntity.fromPb(entityPb);
+      }
+    };
+
+    public static final Type FULL =
+        new Type(DatastoreV1.EntityResult.ResultType.FULL, Entity.class) {
+
+      private static final long serialVersionUID = 7712959777507168274L;
+
+      @Override protected Entity convert(DatastoreV1.Entity entityPb) {
+        return Entity.fromPb(entityPb);
+      }
+    };
+
+    public static final Type KEY_ONLY =
+        new Type(DatastoreV1.EntityResult.ResultType.KEY_ONLY, Key.class) {
+
+      private static final long serialVersionUID = -8514289244104446252L;
+
+      @Override protected Key convert(DatastoreV1.Entity entityPb) {
+        return Key.fromPb(entityPb.getKey());
+      }
+    };
 
-    private final Class value;
+    public static final Type PROJECTION = new Type(
+        DatastoreV1.EntityResult.ResultType.PROJECTION, ProjectionEntity.class) {
 
-    private ResultClass(Class value) {
-      this.value = checkNotNull(value);
+          private static final long serialVersionUID = -7591409419690650246L;
+
+          @Override protected ProjectionEntity convert(DatastoreV1.Entity entityPb) {
+            return ProjectionEntity.fromPb(entityPb);
+          }
+    };
+
+    private final Class resultClass;
+    private final DatastoreV1.EntityResult.ResultType resultType;
+
+    private Type(DatastoreV1.EntityResult.ResultType typePb, Class resultClass) {
+      this.resultType = typePb;
+      this.resultClass = checkNotNull(resultClass);
+      if (typePb != null) {
+        PB_TO_INSTANCE.put(typePb, this);
+      }
     }
 
-    public Class value() {
-      return value;
+    public Class resultClass() {
+      return resultClass;
     }
 
     @Override
     public int hashCode() {
-      return value.hashCode();
+      return resultClass.hashCode();
     }
 
     @Override
@@ -52,102 +108,39 @@ public boolean equals(Object obj) {
       if (obj == this) {
         return true;
       }
-      if (!(obj instanceof ResultClass)) {
+      if (!(obj instanceof Type)) {
         return false;
       }
-      ResultClass other = (ResultClass) obj;
-      return value.equals(other.value);
+      Type other = (Type) obj;
+      return resultClass.equals(other.resultClass);
     }
 
     @Override
     public String toString() {
       ToStringHelper toStringHelper = MoreObjects.toStringHelper(this);
-      toStringHelper.add("value", value);
+      toStringHelper.add("resultType", resultType);
+      toStringHelper.add("resultClass", resultClass);
       return toStringHelper.toString();
     }
 
-    boolean isAssignableFrom(ResultClass resultClass) {
-      return value.isAssignableFrom(resultClass.value);
+    boolean isAssignableFrom(Type otherType) {
+      return resultClass.isAssignableFrom(otherType.resultClass);
     }
 
-    static ResultClass unknown() {
-      return UNKNOWN;
-    }
+    protected abstract V convert(DatastoreV1.Entity value);
 
-    public static ResultClass full() {
-      return FULL;
+    static Type fromPb(DatastoreV1.EntityResult.ResultType typePb) {
+      return MoreObjects.firstNonNull(PB_TO_INSTANCE.get(typePb), UNKNOWN);
     }
-
-    public static ResultClass projection() {
-      return PROJECTION;
-    }
-
-    public static ResultClass keyOnly() {
-      return KEY_ONLY;
-    }
-  }
-
-  /**
-   * Possible results types are:
-   *   FULL: A complete {@link Entity}.
-   *   PROJECTION: A partial entity, represented by {@link PartialEntity}.
-   *   KEY_ONLY: An entity's {@link Key}.
-   */
-  public static enum Type {
-
-    FULL {
-      @Override
-      @SuppressWarnings("unchecked")
-      Entity convert(DatastoreV1.Entity value) {
-        return Entity.fromPb(value);
-      }
-
-      @Override
-      ResultClass resultClass() {
-        return ResultClass.full();
-      }
-    },
-
-    PROJECTION  {
-
-      @Override
-      @SuppressWarnings("unchecked")
-      PartialEntity convert(DatastoreV1.Entity value) {
-        return PartialEntity.fromPb(value);
-      }
-
-      @Override
-      ResultClass resultClass() {
-        return ResultClass.projection();
-      }
-    },
-
-    KEY_ONLY {
-
-      @Override
-      @SuppressWarnings("unchecked")
-      Key convert(DatastoreV1.Entity value) {
-        return Key.fromPb(value.getKey());
-      }
-
-      @Override
-      ResultClass resultClass() {
-        return ResultClass.keyOnly();
-      }
-    };
-
-    abstract  V convert(DatastoreV1.Entity value);
-
-    abstract ResultClass resultClass();
   }
 
-  Query(ResultClass resultClass, String namespace) {
-    this.resultClass = checkNotNull(resultClass);
+  Query(Type type, String namespace) {
+    this.type = checkNotNull(type);
     this.namespace = namespace;
   }
 
-  ResultClass resultClass() {
-    return resultClass;
+  Type type() {
+    return type;
   }
 
   public String namespace() {
@@ -164,10 +157,10 @@ public String toString() {
 
   @Override
   protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException {
-    return fromPb(resultClass, namespace, bytesPb);
+    return fromPb(type, namespace, bytesPb);
   }
 
-  protected abstract Object fromPb(ResultClass resultClass, String namespace, byte[] bytesPb)
+  protected abstract Object fromPb(Type type, String namespace, byte[] bytesPb)
       throws InvalidProtocolBufferException;
 
   protected abstract void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb);
diff --git a/src/main/java/com/google/gcloud/datastore/QueryResult.java b/src/main/java/com/google/gcloud/datastore/QueryResult.java
index aa615b755f19..3dab5967e81d 100644
--- a/src/main/java/com/google/gcloud/datastore/QueryResult.java
+++ b/src/main/java/com/google/gcloud/datastore/QueryResult.java
@@ -6,8 +6,8 @@
  * The result of a Google Cloud Datastore query submission.
  * When result is not typed it is possible to cast it to its appropriate type according to
  * the {@link #resultClass} value.
- * Results are loaded lazily therefore it is possible to get a DatastoreServiceException
- * upon {@link #hasNext} or {@link #next} calls.
+ * Results are loaded lazily therefore it is possible to get a {@code DatastoreServiceException}
+ * upon {@link Iterator#hasNext hasNext} or {@link Iterator#next next} calls.
  *
  * @param V the type of the results value.
  */
diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
index ef18701bf4c1..808d9e8d7662 100644
--- a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
@@ -1,42 +1,31 @@
 package com.google.gcloud.datastore;
 
-import com.google.api.client.repackaged.com.google.common.base.Preconditions;
 import com.google.api.services.datastore.DatastoreV1;
+import com.google.common.base.Preconditions;
 import com.google.common.collect.AbstractIterator;
-import com.google.common.collect.ImmutableMap;
+import com.google.gcloud.datastore.Query.Type;
 
 import java.util.Iterator;
 
 class QueryResultImpl extends AbstractIterator implements QueryResult {
 
-  private static final ImmutableMap
-      RESULT_TYPE_CONVERTER;
-
   private final DatastoreServiceImpl datastore;
   private final DatastoreV1.ReadOptions readOptionsPb;
   private final DatastoreV1.PartitionId partitionIdPb;
-  private final Query.ResultClass resultClass;
+  private final Query.Type queryType;
   private Query query;
-  private Query.Type type;
+  private Query.Type actualType;
   private DatastoreV1.QueryResultBatch resultPb;
   private Iterator entityResultPbIter;
   //private ByteString cursor; // only available in v1beta3
 
-  static {
-    ImmutableMap.Builder builder =
-        ImmutableMap.builder();
-    for (DatastoreV1.EntityResult.ResultType type : DatastoreV1.EntityResult.ResultType.values()) {
-      builder.put(type, Query.Type.valueOf(type.name()));
-    }
-    RESULT_TYPE_CONVERTER = builder.build();
-  }
 
   QueryResultImpl(DatastoreServiceImpl datastore, DatastoreV1.ReadOptions readOptionsPb,
       Query query) {
     this.datastore = datastore;
     this.readOptionsPb = readOptionsPb;
     this.query = query;
-    resultClass = query.resultClass();
+    queryType = query.type();
     DatastoreV1.PartitionId.Builder pbBuilder = DatastoreV1.PartitionId.newBuilder();
     pbBuilder.setDatasetId(datastore.options().dataset());
     if (query.namespace() != null) {
@@ -55,15 +44,20 @@ private DatastoreV1.QueryResultBatch sendRequest() {
     }
     requestPb.setPartitionId(partitionIdPb);
     query.populatePb(requestPb);
+    System.out.println("***************** KOKO ************************");
+    System.out.println("Request Query: " + requestPb.build());
     resultPb = datastore.run(requestPb.build()).getBatch();
+    System.out.println("Result: " + resultPb);
+    System.out.println("***************** KOKO ************************");
     entityResultPbIter = resultPb.getEntityResultList().iterator();
     // cursor = resultPb.getSkippedCursor(); // only available in v1beta3
-    type = RESULT_TYPE_CONVERTER.get(resultPb.getEntityResultType());
-    Preconditions.checkState(resultClass.isAssignableFrom(type.resultClass()),
-        "Unexpected result type");
+    actualType = Type.fromPb(resultPb.getEntityResultType());
+    Preconditions.checkState(queryType.isAssignableFrom(actualType),
+        "Unexpected result type " + actualType + " vs " + queryType);
     return resultPb;
   }
 
+  @SuppressWarnings("unchecked")
   @Override
   protected T computeNext() {
     while (!entityResultPbIter.hasNext()
@@ -76,12 +70,12 @@ protected T computeNext() {
     }
     DatastoreV1.EntityResult entityResultPb = entityResultPbIter.next();
     //cursor = entityResultPb.getCursor(); // only available in v1beta3
-    return type.convert(entityResultPb.getEntity());
+    return (T) actualType.convert(entityResultPb.getEntity());
   }
 
   @Override
   public Class resultClass() {
-    return type.resultClass().value();
+    return actualType.resultClass();
   }
 
   @Override
diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index 328d8a896d8c..83a20f0efa24 100644
--- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -41,7 +41,7 @@
  * 

A less trivial example of a projection query that returns the first 10 results * of "age" and "name" properties (sorted and grouped by "age") with an age greater than 18 *

 {@code
- *   StructuredQuery query = StructuredQuery.projectionBuilder()
+ *   StructuredQuery query = StructuredQuery.projectionBuilder()
  *       .kind(kind)
  *       .projection(Projection.property("age"), Projection.first("name"))
  *       .filter(PropertyFilter.gt("age", 18))
@@ -49,7 +49,7 @@
  *       .orderBy(OrderBy.asc("age"))
  *       .limit(10)
  *       .build();
- *   QueryResult results = datastore.run(query);
+ *   QueryResult results = datastore.run(query);
  *   ...
  * } 
* @@ -577,7 +577,7 @@ public static Projection first(String property) { static class BaseBuilder> { - private ResultClass resultClass; + private Type type; private String namespace; private String kind; private List projection = new LinkedList<>(); @@ -589,8 +589,8 @@ static class BaseBuilder> { private int offset; private Integer limit; - BaseBuilder(ResultClass resultClass) { - this.resultClass = resultClass; + BaseBuilder(Type type) { + this.type = type; } @SuppressWarnings("unchecked") @@ -730,15 +730,15 @@ public StructuredQuery build() { public static final class Builder extends BaseBuilder> { - public Builder(ResultClass resultClass) { - super(resultClass); + public Builder(Type type) { + super(type); } } public static final class KeyOnlyBuilder extends BaseBuilder { public KeyOnlyBuilder() { - super(ResultClass.keyOnly()); + super(Type.KEY_ONLY); projection(Projection.property(KEY_PROPERTY_NAME)); } @@ -757,10 +757,10 @@ public KeyOnlyQuery build() { } public static final class ProjectionBuilder - extends BaseBuilder { + extends BaseBuilder { public ProjectionBuilder() { - super(ResultClass.projection()); + super(Type.PROJECTION); } @Override @@ -808,7 +808,7 @@ public static final class KeyOnlyQuery extends StructuredQuery { } } - public static final class ProjectionQuery extends StructuredQuery { + public static final class ProjectionQuery extends StructuredQuery { private static final long serialVersionUID = -3333183044486150649L; @@ -830,7 +830,7 @@ public List groupBy() { } StructuredQuery(BaseBuilder builder) { - super(builder.resultClass, builder.namespace); + super(builder.type, builder.namespace); kind = builder.kind; projection = ImmutableList.copyOf(builder.projection); filter = builder.filter; @@ -917,7 +917,7 @@ protected void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb) { @Override protected StructuredQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) { - Builder builder = new Builder<>(resultClass()); + Builder builder = new Builder<>(type()); builder.mergeFrom(toPb()); builder.startCursor(new Cursor(responsePb.getEndCursor())); if (offset > 0 && responsePb.getSkippedResults() < offset) { @@ -965,17 +965,16 @@ protected DatastoreV1.Query toPb() { } @Override - protected Object fromPb(ResultClass resultClass, String namespace, byte[] bytesPb) + protected Object fromPb(Type type, String namespace, byte[] bytesPb) throws InvalidProtocolBufferException { - return fromPb(resultClass, namespace, DatastoreV1.Query.parseFrom(bytesPb)); + return fromPb(type, namespace, DatastoreV1.Query.parseFrom(bytesPb)); } - static StructuredQuery fromPb(ResultClass resultClass, String namespace, - DatastoreV1.Query queryPb) { + static StructuredQuery fromPb(Type type, String namespace, DatastoreV1.Query queryPb) { BaseBuilder builder; - if (resultClass.equals(ResultClass.full())) { + if (type.equals(Type.FULL)) { builder = builder(); - } else if (resultClass.equals(ResultClass.keyOnly())) { + } else if (type.equals(Type.KEY_ONLY)) { builder = keyOnlyBuilder(); } else { builder = projectionBuilder(); @@ -984,7 +983,7 @@ static StructuredQuery fromPb(ResultClass resultClass, String namespace, } public static Builder builder() { - return new Builder<>(ResultClass.full()); + return new Builder<>(Type.FULL); } public static KeyOnlyBuilder keyOnlyBuilder() { diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java index a2e15b311689..8b43f421b9e5 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java @@ -6,7 +6,9 @@ import com.google.gcloud.datastore.TransactionOption.IsolationLevel; import com.google.protobuf.ByteString; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.Map; public final class TransactionImpl extends BatchWriterImpl implements Transaction { @@ -15,11 +17,11 @@ public final class TransactionImpl extends BatchWriterImpl implements Transactio private boolean wasRolledback; TransactionImpl(DatastoreServiceImpl datastore, TransactionOption... options) { - super(datastore, options); + super(datastore, getBatchOptions(options)); DatastoreV1.BeginTransactionRequest.Builder requestPb = DatastoreV1.BeginTransactionRequest.newBuilder(); - Map, BatchWriteOption> optionsMap = - BatchWriteOption.asImmutableMap(options); + Map, TransactionOption> optionsMap = + TransactionOption.asImmutableMap(options); IsolationLevel isolationLevel = (IsolationLevel) optionsMap.get(IsolationLevel.class); if (isolationLevel != null) { requestPb.setIsolationLevel(isolationLevel.level().toPb()); @@ -27,6 +29,17 @@ public final class TransactionImpl extends BatchWriterImpl implements Transactio transaction = datastore.requestTransactionId(requestPb); } + private static BatchWriteOption[] getBatchOptions(TransactionOption... options) { + List batchOptions = new ArrayList<>(options.length); + for (TransactionOption option : options) { + BatchWriteOption batchOption = option.toBatchWriteOption(); + if (batchOption != null) { + batchOptions.add(batchOption); + } + } + return batchOptions.toArray(new BatchWriteOption[batchOptions.size()]); + } + @Override public Entity get(Key key) { Iterator iter = get(key, DatastoreServiceImpl.EMPTY_KEY_ARRAY); diff --git a/src/main/java/com/google/gcloud/datastore/TransactionOption.java b/src/main/java/com/google/gcloud/datastore/TransactionOption.java index 0471929e7fb1..597715df20a8 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionOption.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionOption.java @@ -1,12 +1,36 @@ package com.google.gcloud.datastore; import com.google.api.services.datastore.DatastoreV1; +import com.google.common.collect.ImmutableMap; +import java.io.Serializable; +import java.util.Map; -public abstract class TransactionOption extends BatchWriteOption { + +public abstract class TransactionOption implements Serializable { private static final long serialVersionUID = -1862234444015690375L; + public static final class ForceWrites extends TransactionOption { + + private static final long serialVersionUID = -6873967516988380886L; + + private final boolean force; + + public ForceWrites(boolean force) { + this.force = force; + } + + public boolean force() { + return force; + } + + @Override + BatchWriteOption toBatchWriteOption() { + return new BatchWriteOption.ForceWrites(force); + } + } + public static final class IsolationLevel extends TransactionOption { private static final long serialVersionUID = -5592165378565409515L; @@ -43,6 +67,10 @@ public Level level() { // package protected } + public static ForceWrites forceWrites() { + return new ForceWrites(true); + } + public static IsolationLevel serializable() { return new IsolationLevel(IsolationLevel.Level.SERIALIZABLE); } @@ -50,4 +78,18 @@ public static IsolationLevel serializable() { public static IsolationLevel snapshot() { return new IsolationLevel(IsolationLevel.Level.SNAPSHOT); } + + static Map, TransactionOption> asImmutableMap( + TransactionOption... options) { + ImmutableMap.Builder, TransactionOption> builder = + ImmutableMap.builder(); + for (TransactionOption option : options) { + builder.put(option.getClass(), option); + } + return builder.build(); + } + + BatchWriteOption toBatchWriteOption() { + return null; + } } diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index a7c6626d6b92..f3954cabbc23 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -9,7 +9,7 @@ import static org.junit.Assert.fail; import com.google.common.collect.Maps; -import com.google.gcloud.datastore.Query.ResultClass; +import com.google.gcloud.datastore.Query.Type; import com.google.gcloud.datastore.StructuredQuery.OrderBy; import com.google.gcloud.datastore.StructuredQuery.Projection; import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; @@ -45,13 +45,18 @@ public class DatastoreServiceTest { .addValue(STR_VALUE, BOOL_VALUE) .build(); private static final ListValue LIST_VALUE2 = ListValue.of(Collections.singletonList(KEY_VALUE)); + private static final DateTimeValue DATE_TIME_VALUE = new DateTimeValue(DateTime.now()); private static final PartialEntity PARTIAL_ENTITY1 = PartialEntity.builder(PARTIAL_KEY2) .set("str", STR_VALUE).set("bool", BOOL_VALUE).set("list", LIST_VALUE1).build(); private static final PartialEntity PARTIAL_ENTITY2 = PartialEntity.builder(PARTIAL_ENTITY1) .remove("str").set("bool", true).set("list", LIST_VALUE1.get()).build(); - private static final Entity ENTITY1 = Entity.builder(KEY1).set("str", STR_VALUE) - .set("bool", BOOL_VALUE).set("partial1", EntityValue.of(PARTIAL_ENTITY1)) - .set("list", LIST_VALUE2).build(); + private static final Entity ENTITY1 = Entity.builder(KEY1) + .set("str", STR_VALUE) + .set("date", DATE_TIME_VALUE) + .set("bool", BOOL_VALUE) + .set("partial1", EntityValue.of(PARTIAL_ENTITY1)) + .set("list", LIST_VALUE2) + .build(); private static final Entity ENTITY2 = Entity.builder(ENTITY1).key(KEY2).remove("str") .set("name", "koko").setNull("null").set("age", 20).build(); private static final Entity ENTITY3 = Entity.builder(ENTITY1).key(KEY3).remove("str") @@ -272,7 +277,7 @@ public void testNewBatchWriter() { @Test public void testRunGqlQueryNoCasting() { - Query query1 = GqlQuery.builder(ResultClass.full(), "select * from " + KIND1).build(); + Query query1 = GqlQuery.builder(Type.FULL, "select * from " + KIND1).build(); QueryResult results1 = datastore.run(query1); assertTrue(results1.hasNext()); assertEquals(ENTITY1, results1.next()); @@ -280,7 +285,7 @@ public void testRunGqlQueryNoCasting() { datastore.put(ENTITY3); Query query2 = GqlQuery.builder( - ResultClass.full(), "select * from " + KIND2 + " order by __key__").build(); + Type.FULL, "select * from " + KIND2 + " order by __key__").build(); QueryResult results2 = datastore.run(query2); assertTrue(results2.hasNext()); assertEquals(ENTITY2, results2.next()); @@ -288,24 +293,28 @@ public void testRunGqlQueryNoCasting() { assertEquals(ENTITY3, results2.next()); assertFalse(results2.hasNext()); - query1 = GqlQuery.builder(ResultClass.full(), "select * from bla").build(); + query1 = GqlQuery.builder(Type.FULL, "select * from bla").build(); results1 = datastore.run(query1); assertFalse(results1.hasNext()); Query keyOnlyQuery = - GqlQuery.builder(ResultClass.keyOnly(), "select __key__ from " + KIND1).build(); + GqlQuery.builder(Type.KEY_ONLY, "select __key__ from " + KIND1).build(); QueryResult keyOnlyResults = datastore.run(keyOnlyQuery); assertTrue(keyOnlyResults.hasNext()); assertEquals(KEY1, keyOnlyResults.next()); assertFalse(keyOnlyResults.hasNext()); - Query projectionQuery = GqlQuery.builder( - ResultClass.projection(), "select str from " + KIND1).build(); - QueryResult projectionResult = datastore.run(projectionQuery); + // broken because of b/18806697 + Query projectionQuery = GqlQuery.builder( + Type.PROJECTION, "select str, date from " + KIND1).build(); + QueryResult projectionResult = datastore.run(projectionQuery); assertTrue(projectionResult.hasNext()); - PartialEntity partialEntity = projectionResult.next(); - assertEquals("str", partialEntity.getString("str")); - assertEquals(1, partialEntity.names().size()); + ProjectionEntity projectionEntity = projectionResult.next(); + assertEquals("str", projectionEntity.getString("str")); + assertEquals(DATE_TIME_VALUE, projectionEntity.getDateTime("date")); + assertEquals(DATE_TIME_VALUE.get().timestampMicroseconds(), + projectionEntity.getLong("date")); + assertEquals(2, projectionEntity.names().size()); assertFalse(projectionResult.hasNext()); } @@ -343,7 +352,7 @@ public void testRunStructuredQuery() { assertEquals(ENTITY1.key(), results2.next()); assertFalse(results2.hasNext()); - StructuredQuery projectionQuery = StructuredQuery.projectionBuilder() + StructuredQuery projectionQuery = StructuredQuery.projectionBuilder() .kind(KIND2) .projection(Projection.property("age"), Projection.first("name")) .filter(PropertyFilter.gt("age", 18)) @@ -352,9 +361,10 @@ public void testRunStructuredQuery() { .limit(10) .build(); - QueryResult results3 = datastore.run(projectionQuery); + // broken because of b/18806697 + QueryResult results3 = datastore.run(projectionQuery); assertTrue(results3.hasNext()); - PartialEntity entity = results3.next(); + ProjectionEntity entity = results3.next(); assertEquals(ENTITY2.key(), entity.key()); assertEquals(20, entity.getLong("age")); assertEquals("koko", entity.getString("name")); @@ -419,11 +429,14 @@ public void testGet() { StringValue value1 = entity.getValue("str"); BooleanValue value2 = entity.getValue("bool"); ListValue value3 = entity.getValue("list"); - assertEquals(value1, STR_VALUE); - assertEquals(value2, BOOL_VALUE); - assertEquals(value3, LIST_VALUE2); - assertEquals(PARTIAL_ENTITY1, entity.getEntity("partial1")); - assertEquals(4, entity.names().size()); + DateTimeValue value4 = entity.getValue("date"); + PartialEntity value5 = entity.getEntity("partial1"); + assertEquals(STR_VALUE, value1); + assertEquals(BOOL_VALUE, value2); + assertEquals(LIST_VALUE2, value3); + assertEquals(DATE_TIME_VALUE, value4); + assertEquals(PARTIAL_ENTITY1, value5); + assertEquals(5, entity.names().size()); assertFalse(entity.contains("bla")); } @@ -445,7 +458,7 @@ public void testGetArray() { assertEquals(partial1, PARTIAL_ENTITY2); assertEquals(partial2, ENTITY2); assertEquals(Value.Type.BOOLEAN, entity3.type("bool")); - assertEquals(5, entity3.names().size()); + assertEquals(6, entity3.names().size()); assertFalse(entity3.contains("bla")); try { entity3.getString("str"); diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index f66bb6bc319a..27ec3b600d58 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -7,7 +7,6 @@ import com.google.api.services.datastore.DatastoreV1; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; -import com.google.gcloud.datastore.Query.ResultClass; import com.google.gcloud.datastore.StructuredQuery.CompositeFilter; import com.google.gcloud.datastore.StructuredQuery.OrderBy; import com.google.gcloud.datastore.StructuredQuery.Projection; @@ -40,7 +39,7 @@ public class SerializationTest { .namespace("ns1") .build(); private static final Query GQL2 = - GqlQuery.builder(ResultClass.full(), "select * from kind1 where name = @name and age > @1") + GqlQuery.builder(Query.Type.FULL, "select * from kind1 where name = @name and age > @1") .setArgument("name", "name1") .addArgument(20) .namespace("ns1") @@ -50,7 +49,7 @@ public class SerializationTest { .kind("k") .filter(PropertyFilter.eq("p1", "hello")) .build(); - private static final Query QUERY3 = StructuredQuery.projectionBuilder() + private static final Query QUERY3 = StructuredQuery.projectionBuilder() .kind("k") .namespace("ns1") .projection(Projection.property("p")) @@ -95,6 +94,7 @@ public class SerializationTest { .addValue(STRING_VALUE) .addValue(new NullValue()) .build(); + private static final ProjectionEntity PROJECTION_ENTITY = ProjectionEntity.fromPb(ENTITY1.toPb()); @SuppressWarnings("rawtypes") private Multimap typeToValues = ImmutableMultimap.builder() @@ -129,8 +129,8 @@ public void testValues() throws Exception { @Test public void testTypes() throws Exception { Object[] types = { KEY1, KEY2, INCOMPLETE_KEY1, INCOMPLETE_KEY2, ENTITY1, ENTITY2, - ENTITY3, EMBEDDED_ENTITY1, EMBEDDED_ENTITY2, EMBEDDED_ENTITY3, DATE_TIME1, - BLOB1, CURSOR1, GQL1, GQL2, QUERY1, QUERY2, QUERY3}; + ENTITY3, EMBEDDED_ENTITY1, EMBEDDED_ENTITY2, EMBEDDED_ENTITY3, PROJECTION_ENTITY, + DATE_TIME1, BLOB1, CURSOR1, GQL1, GQL2, QUERY1, QUERY2, QUERY3}; for (Object obj : types) { Object copy = serialiazeAndDeserialize(obj); assertEquals(obj, obj); From 72a1192efae97ee0b0c05b9d18340094c612fa88 Mon Sep 17 00:00:00 2001 From: aozarov Date: Fri, 19 Dec 2014 09:37:10 -0800 Subject: [PATCH 065/771] Add a hack to the test to by-pass datastore issue with projection query --- pom.xml | 6 +++ .../datastore/DatastoreServiceFactory.java | 7 +-- .../datastore/DatastoreServiceOptions.java | 16 +++++- .../datastore/DatastoreServiceTest.java | 50 ++++++++++++++++--- 4 files changed, 66 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index 1fa28d56c920..1f8cb504a65c 100644 --- a/pom.xml +++ b/pom.xml @@ -65,6 +65,12 @@ google-api-services-datastore v1beta2-rev23-1.19.0 + + org.easymock + easymock + 3.3 + test + diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java index 2ae4a3011836..9a8bb1dd82bc 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java @@ -1,8 +1,5 @@ package com.google.gcloud.datastore; -import com.google.api.services.datastore.client.Datastore; -import com.google.api.services.datastore.client.DatastoreFactory; -import com.google.api.services.datastore.client.DatastoreOptions; public abstract class DatastoreServiceFactory { @@ -10,9 +7,7 @@ public abstract class DatastoreServiceFactory { private static final DatastoreServiceFactory INSTANCE = new DatastoreServiceFactory() { @Override public DatastoreService get(DatastoreServiceOptions options) { - DatastoreOptions dsOptions = options.toDatastoreOptions(); - Datastore datastore = DatastoreFactory.get().create(dsOptions); - return new DatastoreServiceImpl(options, datastore); + return new DatastoreServiceImpl(options, options.datastore()); } }; diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java index 4931bd62af71..3b81cac646c4 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java @@ -5,6 +5,8 @@ import static com.google.gcloud.datastore.Validator.validateDataset; import static com.google.gcloud.datastore.Validator.validateNamespace; +import com.google.api.services.datastore.client.Datastore; +import com.google.api.services.datastore.client.DatastoreFactory; import com.google.api.services.datastore.client.DatastoreOptions; import com.google.common.collect.ImmutableSet; import com.google.gcloud.ServiceOptions; @@ -20,6 +22,7 @@ public class DatastoreServiceOptions extends ServiceOptions { private final String dataset; private final String namespace; private final boolean force; + private final Datastore datastore; DatastoreServiceOptions(Builder builder) { super(builder); @@ -27,6 +30,7 @@ public class DatastoreServiceOptions extends ServiceOptions { checkArgument(dataset != null, "missing dataset"); namespace = builder.namespace != null ? builder.namespace : defaultNamespace(); force = builder.force; + datastore = builder.datastore; } public static class Builder extends ServiceOptions.Builder { @@ -34,6 +38,7 @@ public static class Builder extends ServiceOptions.Builder { private String dataset; private String namespace; private boolean force = false; + private Datastore datastore; private Builder() { } @@ -49,6 +54,11 @@ public DatastoreServiceOptions build() { return new DatastoreServiceOptions(this); } + Builder datastore(Datastore datastore) { + this.datastore = datastore; + return this; + } + public Builder dataset(String dataset) { this.dataset = validateDataset(dataset); return this; @@ -100,7 +110,7 @@ public Builder toBuilder() { return new Builder(this); } - DatastoreOptions toDatastoreOptions() { + private DatastoreOptions toDatastoreOptions() { return new DatastoreOptions.Builder() .dataset(dataset()) .host(host()) @@ -108,6 +118,10 @@ DatastoreOptions toDatastoreOptions() { .build(); } + Datastore datastore() { + return datastore != null ? datastore : DatastoreFactory.get().create(toDatastoreOptions()); + } + public static Builder builder() { return new Builder(); } diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index f3954cabbc23..4465ead8abb6 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -8,12 +8,16 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.api.services.datastore.DatastoreV1; +import com.google.api.services.datastore.client.Datastore; +import com.google.api.services.datastore.client.DatastoreException; import com.google.common.collect.Maps; import com.google.gcloud.datastore.Query.Type; import com.google.gcloud.datastore.StructuredQuery.OrderBy; import com.google.gcloud.datastore.StructuredQuery.Projection; import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; +import org.easymock.EasyMock; import org.junit.Before; import org.junit.Test; @@ -276,7 +280,7 @@ public void testNewBatchWriter() { } @Test - public void testRunGqlQueryNoCasting() { + public void testRunGqlQueryNoCasting() throws DatastoreException { Query query1 = GqlQuery.builder(Type.FULL, "select * from " + KIND1).build(); QueryResult results1 = datastore.run(query1); assertTrue(results1.hasNext()); @@ -304,18 +308,35 @@ public void testRunGqlQueryNoCasting() { assertEquals(KEY1, keyOnlyResults.next()); assertFalse(keyOnlyResults.hasNext()); - // broken because of b/18806697 - Query projectionQuery = GqlQuery.builder( + GqlQuery projectionQuery = GqlQuery.builder( Type.PROJECTION, "select str, date from " + KIND1).build(); + + // this hack is needed because of b/18806697 + DatastoreV1.RunQueryRequest.Builder requestPb = DatastoreV1.RunQueryRequest.newBuilder(); + requestPb.setGqlQuery(projectionQuery.toPb()); + requestPb.setPartitionId(DatastoreV1.PartitionId.newBuilder().setDatasetId(DATASET).build()); + DatastoreV1.RunQueryResponse responsePb = + ((DatastoreServiceImpl) datastore).run(requestPb.build()); + DatastoreV1.RunQueryResponse.Builder responsePbBuilder = responsePb.toBuilder(); + responsePbBuilder.getBatchBuilder() + .setEntityResultType(DatastoreV1.EntityResult.ResultType.PROJECTION).build(); + Datastore mockDatastore = EasyMock.createMock(Datastore.class); + EasyMock.expect(mockDatastore.runQuery(requestPb.build())).andReturn(responsePbBuilder.build()); + EasyMock.replay(mockDatastore); + datastore = DatastoreServiceFactory.getDefault( + datastore.options().toBuilder().datastore(mockDatastore).build()); + // end of hack + QueryResult projectionResult = datastore.run(projectionQuery); assertTrue(projectionResult.hasNext()); ProjectionEntity projectionEntity = projectionResult.next(); assertEquals("str", projectionEntity.getString("str")); - assertEquals(DATE_TIME_VALUE, projectionEntity.getDateTime("date")); + assertEquals(DATE_TIME_VALUE.get(), projectionEntity.getDateTime("date")); assertEquals(DATE_TIME_VALUE.get().timestampMicroseconds(), projectionEntity.getLong("date")); assertEquals(2, projectionEntity.names().size()); assertFalse(projectionResult.hasNext()); + EasyMock.verify(mockDatastore); } @Test @@ -338,7 +359,7 @@ public void testRunGqlQueryWithCasting() { } @Test - public void testRunStructuredQuery() { + public void testRunStructuredQuery() throws DatastoreException { StructuredQuery query = StructuredQuery.builder().kind(KIND1).orderBy(OrderBy.asc("__key__")).build(); QueryResult results1 = datastore.run(query); @@ -361,7 +382,22 @@ public void testRunStructuredQuery() { .limit(10) .build(); - // broken because of b/18806697 + // this hack is needed because of b/18806697 + DatastoreV1.RunQueryRequest.Builder requestPb = DatastoreV1.RunQueryRequest.newBuilder(); + requestPb.setQuery(projectionQuery.toPb()); + requestPb.setPartitionId(DatastoreV1.PartitionId.newBuilder().setDatasetId(DATASET).build()); + DatastoreV1.RunQueryResponse responsePb = + ((DatastoreServiceImpl) datastore).run(requestPb.build()); + DatastoreV1.RunQueryResponse.Builder responsePbBuilder = responsePb.toBuilder(); + responsePbBuilder.getBatchBuilder() + .setEntityResultType(DatastoreV1.EntityResult.ResultType.PROJECTION).build(); + Datastore mockDatastore = EasyMock.createMock(Datastore.class); + EasyMock.expect(mockDatastore.runQuery(requestPb.build())).andReturn(responsePbBuilder.build()); + EasyMock.replay(mockDatastore); + datastore = DatastoreServiceFactory.getDefault( + datastore.options().toBuilder().datastore(mockDatastore).build()); + // end of hack + QueryResult results3 = datastore.run(projectionQuery); assertTrue(results3.hasNext()); ProjectionEntity entity = results3.next(); @@ -370,6 +406,7 @@ public void testRunStructuredQuery() { assertEquals("koko", entity.getString("name")); assertEquals(2, entity.properties().size()); assertFalse(results3.hasNext()); + EasyMock.verify(mockDatastore); // TODO(ozarov): construct a test to verify nextQuery/pagination } @@ -467,6 +504,7 @@ public void testGetArray() { // expected - no such property } assertFalse(result.hasNext()); + // TODO(ozarov): construct a test to verify more results } public List fetch(Key key, Key... others) { From ad977279e7d9c4da9c79d6855dd8b6d3a1141de2 Mon Sep 17 00:00:00 2001 From: aozarov Date: Fri, 19 Dec 2014 10:01:49 -0800 Subject: [PATCH 066/771] remove printout leftover --- .../java/com/google/gcloud/datastore/QueryResultImpl.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java index 808d9e8d7662..4d92ec80ed72 100644 --- a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java +++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java @@ -44,11 +44,7 @@ private DatastoreV1.QueryResultBatch sendRequest() { } requestPb.setPartitionId(partitionIdPb); query.populatePb(requestPb); - System.out.println("***************** KOKO ************************"); - System.out.println("Request Query: " + requestPb.build()); resultPb = datastore.run(requestPb.build()).getBatch(); - System.out.println("Result: " + resultPb); - System.out.println("***************** KOKO ************************"); entityResultPbIter = resultPb.getEntityResultList().iterator(); // cursor = resultPb.getSkippedCursor(); // only available in v1beta3 actualType = Type.fromPb(resultPb.getEntityResultType()); From 8ad09d4334532375be8963a278582248fb92646a Mon Sep 17 00:00:00 2001 From: aozarov Date: Fri, 19 Dec 2014 10:52:41 -0800 Subject: [PATCH 067/771] 1. Renamed nameArg and numberArg to namedBinding and positionalBinding 2. Allow projection query to accept KEY_ONLY or FULL results. --- .../com/google/gcloud/datastore/GqlQuery.java | 146 +++++++++--------- .../gcloud/datastore/QueryResultImpl.java | 4 + .../gcloud/datastore/StructuredQuery.java | 2 - .../datastore/DatastoreServiceTest.java | 27 +++- .../gcloud/datastore/SerializationTest.java | 8 +- 5 files changed, 103 insertions(+), 84 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java index c80cb4a77f14..8569bae8c674 100644 --- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java +++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java @@ -58,10 +58,10 @@ public final class GqlQuery extends Query { private final transient String queryString; private final transient boolean allowLiteral; - private final transient ImmutableList nameArgs; - private final transient ImmutableList numberArgs; + private final transient ImmutableList namedBindings; + private final transient ImmutableList positionalBindings; - static final class Argument extends Serializable { + static final class Binding extends Serializable { private static final long serialVersionUID = 1976895435257636275L; @@ -69,13 +69,13 @@ static final class Argument extends Serializable { private final transient Cursor cursor; private final transient Value value; - Argument(String name, Cursor cursor) { + Binding(String name, Cursor cursor) { this.name = name; this.cursor = checkNotNull(cursor); value = null; } - Argument(String name, Value value) { + Binding(String name, Value value) { this.name = name; this.value = checkNotNull(value); cursor = null; @@ -99,10 +99,10 @@ public boolean equals(Object obj) { if (obj == this) { return true; } - if (!(obj instanceof Argument)) { + if (!(obj instanceof Binding)) { return false; } - Argument other = (Argument) obj; + Binding other = (Binding) obj; return Objects.equals(name, other.name) && Objects.equals(cursor, other.cursor) && Objects.equals(value, other.value); @@ -128,12 +128,12 @@ protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { return fromPb(DatastoreV1.GqlQueryArg.parseFrom(bytesPb)); } - static Argument fromPb(DatastoreV1.GqlQueryArg argPb) { + static Binding fromPb(DatastoreV1.GqlQueryArg argPb) { String name = argPb.hasName() ? argPb.getName() : null; if (argPb.hasCursor()) { - return new Argument(name, new Cursor(argPb.getCursor())); + return new Binding(name, new Cursor(argPb.getCursor())); } - return new Argument(name, Value.fromPb(argPb.getValue())); + return new Binding(name, Value.fromPb(argPb.getValue())); } } @@ -146,8 +146,8 @@ public static final class Builder { private String namespace; private String queryString; private boolean allowLiteral; - private Map nameArgs = new TreeMap<>(); - private List numberArgs = new LinkedList<>(); + private Map namedBindings = new TreeMap<>(); + private List positionalBindings = new LinkedList<>(); Builder(Type type, String query) { this.type = checkNotNull(type); @@ -169,99 +169,99 @@ public Builder allowLiteral(boolean allowLiteral) { return this; } - public Builder clearArguments() { - nameArgs.clear(); - numberArgs.clear(); + public Builder clearBindings() { + namedBindings.clear(); + positionalBindings.clear(); return this; } - public Builder setArgument(String name, Cursor cursor) { - nameArgs.put(name, new Argument(name, cursor)); + public Builder setBinding(String name, Cursor cursor) { + namedBindings.put(name, new Binding(name, cursor)); return this; } - public Builder setArgument(String name, String... value) { - nameArgs.put(name, toArgument(name, StringValue.MARSHALLER, Arrays.asList(value))); + public Builder setBinding(String name, String... value) { + namedBindings.put(name, toBinding(name, StringValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder setArgument(String name, long... value) { - nameArgs.put(name, toArgument(name, LongValue.MARSHALLER, Longs.asList(value))); + public Builder setBinding(String name, long... value) { + namedBindings.put(name, toBinding(name, LongValue.MARSHALLER, Longs.asList(value))); return this; } - public Builder setArgument(String name, double... value) { - nameArgs.put(name, toArgument(name, DoubleValue.MARSHALLER, Doubles.asList(value))); + public Builder setBinding(String name, double... value) { + namedBindings.put(name, toBinding(name, DoubleValue.MARSHALLER, Doubles.asList(value))); return this; } - public Builder setArgument(String name, boolean... value) { - nameArgs.put(name, toArgument(name, BooleanValue.MARSHALLER, Booleans.asList(value))); + public Builder setBinding(String name, boolean... value) { + namedBindings.put(name, toBinding(name, BooleanValue.MARSHALLER, Booleans.asList(value))); return this; } - public Builder setArgument(String name, DateTime... value) { - nameArgs.put(name, toArgument(name, DateTimeValue.MARSHALLER, Arrays.asList(value))); + public Builder setBinding(String name, DateTime... value) { + namedBindings.put(name, toBinding(name, DateTimeValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder setArgument(String name, Key... value) { - nameArgs.put(name, toArgument(name, KeyValue.MARSHALLER, Arrays.asList(value))); + public Builder setBinding(String name, Key... value) { + namedBindings.put(name, toBinding(name, KeyValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder setArgument(String name, PartialEntity... value) { - nameArgs.put(name, toArgument(name, EntityValue.MARSHALLER, Arrays.asList(value))); + public Builder setBinding(String name, PartialEntity... value) { + namedBindings.put(name, toBinding(name, EntityValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder setArgument(String name, Blob... value) { - nameArgs.put(name, toArgument(name, BlobValue.MARSHALLER, Arrays.asList(value))); + public Builder setBinding(String name, Blob... value) { + namedBindings.put(name, toBinding(name, BlobValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(Cursor cursor) { - numberArgs.add(new Argument(null, cursor)); + public Builder addBinding(Cursor cursor) { + positionalBindings.add(new Binding(null, cursor)); return this; } - public Builder addArgument(String... value) { - numberArgs.add(toArgument(StringValue.MARSHALLER, Arrays.asList(value))); + public Builder addBinding(String... value) { + positionalBindings.add(toBinding(StringValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(long... value) { - numberArgs.add(toArgument(LongValue.MARSHALLER, Longs.asList(value))); + public Builder addBinding(long... value) { + positionalBindings.add(toBinding(LongValue.MARSHALLER, Longs.asList(value))); return this; } - public Builder addArgument(double... value) { - numberArgs.add(toArgument(DoubleValue.MARSHALLER, Doubles.asList(value))); + public Builder addBinding(double... value) { + positionalBindings.add(toBinding(DoubleValue.MARSHALLER, Doubles.asList(value))); return this; } - public Builder addArgument(boolean... value) { - numberArgs.add(toArgument(BooleanValue.MARSHALLER, Booleans.asList(value))); + public Builder addBinding(boolean... value) { + positionalBindings.add(toBinding(BooleanValue.MARSHALLER, Booleans.asList(value))); return this; } - public Builder addArgument(DateTime... value) { - numberArgs.add(toArgument(DateTimeValue.MARSHALLER, Arrays.asList(value))); + public Builder addBinding(DateTime... value) { + positionalBindings.add(toBinding(DateTimeValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(Key... value) { - numberArgs.add(toArgument(KeyValue.MARSHALLER, Arrays.asList(value))); + public Builder addBinding(Key... value) { + positionalBindings.add(toBinding(KeyValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(PartialEntity... value) { - numberArgs.add(toArgument(EntityValue.MARSHALLER, Arrays.asList(value))); + public Builder addBinding(PartialEntity... value) { + positionalBindings.add(toBinding(EntityValue.MARSHALLER, Arrays.asList(value))); return this; } - public Builder addArgument(Blob... value) { - numberArgs.add(toArgument(BlobValue.MARSHALLER, Arrays.asList(value))); + public Builder addBinding(Blob... value) { + positionalBindings.add(toBinding(BlobValue.MARSHALLER, Arrays.asList(value))); return this; } @@ -270,12 +270,12 @@ public GqlQuery build() { } @SuppressWarnings("rawtypes") - private static Argument toArgument(Value.BuilderFactory builderFactory, List values) { - return toArgument(null, builderFactory, values); + private static Binding toBinding(Value.BuilderFactory builderFactory, List values) { + return toBinding(null, builderFactory, values); } @SuppressWarnings({"unchecked", "rawtypes"}) - private static Argument toArgument(String name, Value.BuilderFactory builderFactory, + private static Binding toBinding(String name, Value.BuilderFactory builderFactory, List values) { List> list = new ArrayList<>(values.size()); for (Object object : values) { @@ -289,7 +289,7 @@ private static Argument toArgument(String name, Value.BuilderFactory builderFact } else { value = new ListValue(list); } - return new Argument(name, value); + return new Binding(name, value); } } @@ -297,8 +297,8 @@ private GqlQuery(Builder builder) { super(builder.type, builder.namespace); queryString = builder.queryString; allowLiteral = builder.allowLiteral; - nameArgs = ImmutableList.copyOf(builder.nameArgs.values()); - numberArgs = ImmutableList.copyOf(builder.numberArgs); + namedBindings = ImmutableList.copyOf(builder.namedBindings.values()); + positionalBindings = ImmutableList.copyOf(builder.positionalBindings); } public String queryString() { @@ -310,30 +310,30 @@ public boolean allowLiteral() { } /** - * Returns an immutable map of named arguments. + * Returns an immutable map of named bindings. */ - public Map nameArgs() { + public Map namedBindings() { ImmutableMap.Builder builder = ImmutableSortedMap.naturalOrder(); - for (Argument argument : nameArgs) { - builder.put(argument.name(), argument.cursorOrValue()); + for (Binding binding : namedBindings) { + builder.put(binding.name(), binding.cursorOrValue()); } return builder.build(); } /** - * Returns an immutable list of numbered arguments (using original order). + * Returns an immutable list of positional bindings (using original order). */ public List numberArgs() { ImmutableList.Builder builder = ImmutableList.builder(); - for (Argument argument : numberArgs) { - builder.add(argument.cursorOrValue()); + for (Binding binding : positionalBindings) { + builder.add(binding.cursorOrValue()); } return builder.build(); } @Override public int hashCode() { - return Objects.hash(namespace(), queryString, allowLiteral, nameArgs, numberArgs); + return Objects.hash(namespace(), queryString, allowLiteral, namedBindings, positionalBindings); } @Override @@ -348,8 +348,8 @@ public boolean equals(Object obj) { return Objects.equals(namespace(), other.namespace()) && Objects.equals(queryString, other.queryString) && allowLiteral == other.allowLiteral - && Objects.equals(nameArgs, other.nameArgs) - && Objects.equals(numberArgs, other.numberArgs); + && Objects.equals(namedBindings, other.namedBindings) + && Objects.equals(positionalBindings, other.positionalBindings); } @Override @@ -357,10 +357,10 @@ protected DatastoreV1.GqlQuery toPb() { DatastoreV1.GqlQuery.Builder queryPb = DatastoreV1.GqlQuery.newBuilder(); queryPb.setQueryString(queryString); queryPb.setAllowLiteral(allowLiteral); - for (Argument argument : nameArgs) { + for (Binding argument : namedBindings) { queryPb.addNameArg(argument.toPb()); } - for (Argument argument : numberArgs) { + for (Binding argument : positionalBindings) { queryPb.addNumberArg(argument.toPb()); } return queryPb.build(); @@ -391,12 +391,12 @@ static GqlQuery fromPb(Type resultType, String namespace, builder.allowLiteral = queryPb.getAllowLiteral(); } for (DatastoreV1.GqlQueryArg nameArg : queryPb.getNameArgList()) { - Argument argument = Argument.fromPb(nameArg); - builder.nameArgs.put(argument.name(), argument); + Binding argument = Binding.fromPb(nameArg); + builder.namedBindings.put(argument.name(), argument); } for (DatastoreV1.GqlQueryArg numberArg : queryPb.getNumberArgList()) { - Argument argument = Argument.fromPb(numberArg); - builder.numberArgs.add(argument); + Binding argument = Binding.fromPb(numberArg); + builder.positionalBindings.add(argument); } return builder.build(); } diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java index 4d92ec80ed72..5639eec89195 100644 --- a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java +++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java @@ -48,6 +48,10 @@ private DatastoreV1.QueryResultBatch sendRequest() { entityResultPbIter = resultPb.getEntityResultList().iterator(); // cursor = resultPb.getSkippedCursor(); // only available in v1beta3 actualType = Type.fromPb(resultPb.getEntityResultType()); + if (queryType == Type.PROJECTION) { + // projection entity can represent all type of results + actualType = Type.PROJECTION; + } Preconditions.checkState(queryType.isAssignableFrom(actualType), "Unexpected result type " + actualType + " vs " + queryType); return resultPb; diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java index 83a20f0efa24..84d7d65e2c25 100644 --- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java +++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java @@ -814,8 +814,6 @@ public static final class ProjectionQuery extends StructuredQuery keyProjectionQuery = GqlQuery.builder( + Type.PROJECTION, "select __key__ from " + KIND1).build(); + QueryResult keyProjectionResult = datastore.run(keyProjectionQuery); + assertTrue(keyProjectionResult.hasNext()); + ProjectionEntity p = keyProjectionResult.next(); + assertEquals(KEY1, p.key()); + assertTrue(p.properties().isEmpty()); + assertFalse(keyProjectionResult.hasNext()); + GqlQuery projectionQuery = GqlQuery.builder( Type.PROJECTION, "select str, date from " + KIND1).build(); @@ -373,6 +382,15 @@ public void testRunStructuredQuery() throws DatastoreException { assertEquals(ENTITY1.key(), results2.next()); assertFalse(results2.hasNext()); + StructuredQuery keyOnlyProjectionQuery = StructuredQuery.projectionBuilder() + .kind(KIND1).projection(Projection.property("__key__")).build(); + QueryResult results3 = datastore.run(keyOnlyProjectionQuery); + assertTrue(results3.hasNext()); + ProjectionEntity projectionEntity = results3.next(); + assertEquals(ENTITY1.key(), projectionEntity.key()); + assertTrue(projectionEntity.names().isEmpty()); + assertFalse(results2.hasNext()); + StructuredQuery projectionQuery = StructuredQuery.projectionBuilder() .kind(KIND2) .projection(Projection.property("age"), Projection.first("name")) @@ -381,7 +399,6 @@ public void testRunStructuredQuery() throws DatastoreException { .orderBy(OrderBy.asc("age")) .limit(10) .build(); - // this hack is needed because of b/18806697 DatastoreV1.RunQueryRequest.Builder requestPb = DatastoreV1.RunQueryRequest.newBuilder(); requestPb.setQuery(projectionQuery.toPb()); @@ -398,14 +415,14 @@ public void testRunStructuredQuery() throws DatastoreException { datastore.options().toBuilder().datastore(mockDatastore).build()); // end of hack - QueryResult results3 = datastore.run(projectionQuery); - assertTrue(results3.hasNext()); - ProjectionEntity entity = results3.next(); + QueryResult results4 = datastore.run(projectionQuery); + assertTrue(results4.hasNext()); + ProjectionEntity entity = results4.next(); assertEquals(ENTITY2.key(), entity.key()); assertEquals(20, entity.getLong("age")); assertEquals("koko", entity.getString("name")); assertEquals(2, entity.properties().size()); - assertFalse(results3.hasNext()); + assertFalse(results4.hasNext()); EasyMock.verify(mockDatastore); // TODO(ozarov): construct a test to verify nextQuery/pagination diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 27ec3b600d58..706c247c6623 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -34,14 +34,14 @@ public class SerializationTest { private static final Cursor CURSOR2 = Cursor.copyFrom(new byte[] {10}); private static final Query GQL1 = GqlQuery.builder("select * from kind1 where name = @name and age > @1") - .setArgument("name", "name1") - .addArgument(20) + .setBinding("name", "name1") + .addBinding(20) .namespace("ns1") .build(); private static final Query GQL2 = GqlQuery.builder(Query.Type.FULL, "select * from kind1 where name = @name and age > @1") - .setArgument("name", "name1") - .addArgument(20) + .setBinding("name", "name1") + .addBinding(20) .namespace("ns1") .build(); private static final Query QUERY1 = StructuredQuery.builder().kind("kind1").build(); From 2105cb7514508a68a2d9819b721ec8c5bd78029a Mon Sep 17 00:00:00 2001 From: aozarov Date: Fri, 19 Dec 2014 15:49:54 -0800 Subject: [PATCH 068/771] Change key API, to make it clear that kind (for partial) or (kind, name) (kind, id) are part of the path and remove addAncestor from builder as setter only make the path order more clear. --- .../google/gcloud/datastore/BaseEntity.java | 16 ---- .../com/google/gcloud/datastore/BaseKey.java | 74 ++++++++----------- .../java/com/google/gcloud/datastore/Key.java | 45 +++++------ .../google/gcloud/datastore/KeyFactory.java | 24 +++--- .../google/gcloud/datastore/PartialKey.java | 49 +++++------- .../google/gcloud/datastore/PathElement.java | 41 ++++++---- .../google/gcloud/ExceptionHandlerTest.java | 1 - .../datastore/DatastoreServiceTest.java | 21 +++--- .../gcloud/datastore/SerializationTest.java | 4 +- 9 files changed, 120 insertions(+), 155 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/BaseEntity.java b/src/main/java/com/google/gcloud/datastore/BaseEntity.java index 0ec36a2f0211..91665fd59987 100644 --- a/src/main/java/com/google/gcloud/datastore/BaseEntity.java +++ b/src/main/java/com/google/gcloud/datastore/BaseEntity.java @@ -13,7 +13,6 @@ import com.google.api.services.datastore.DatastoreV1; import com.google.common.collect.ImmutableSortedMap; -import com.google.gcloud.datastore.Value.Type; import java.util.HashMap; import java.util.List; @@ -150,10 +149,6 @@ public > V getValue(String name) { return property; } - public Type type(String name) { - return getValue(name).type(); - } - public boolean isNull(String name) { return getValue(name) instanceof NullValue; } @@ -195,17 +190,6 @@ public Blob getBlob(String name) { return ((BlobValue) getValue(name)).get(); } - /** - * Returns the property's value as a {@link RawValue}. - */ - public RawValue asRawValue(String name) { - Value value = getValue(name); - if (value instanceof RawValue) { - return (RawValue) value; - } - return new RawValue(value.toPb()); - } - /** * Returns the properties name. */ diff --git a/src/main/java/com/google/gcloud/datastore/BaseKey.java b/src/main/java/com/google/gcloud/datastore/BaseKey.java index d8a7a553c157..f9d961034941 100644 --- a/src/main/java/com/google/gcloud/datastore/BaseKey.java +++ b/src/main/java/com/google/gcloud/datastore/BaseKey.java @@ -1,13 +1,11 @@ package com.google.gcloud.datastore; -import static com.google.common.base.Preconditions.checkArgument; import static com.google.gcloud.datastore.Validator.validateDataset; import static com.google.gcloud.datastore.Validator.validateKind; import static com.google.gcloud.datastore.Validator.validateNamespace; import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Preconditions; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import java.util.LinkedList; @@ -23,8 +21,7 @@ abstract class BaseKey extends Serializable { private final transient String dataset; private final transient String namespace; - private final transient ImmutableList ancestors; - private final transient String kind; + private final transient ImmutableList path; abstract static class Builder> { @@ -37,15 +34,15 @@ abstract static class Builder> { public Builder(String dataset, String kind) { this.dataset = validateDataset(dataset); - this.kind = validateKind(kind); ancestors = new LinkedList<>(); + this.kind = validateKind(kind); } public Builder(BaseKey copyFrom) { dataset = copyFrom.dataset(); namespace = copyFrom.namespace(); - kind = copyFrom.kind(); ancestors = new LinkedList<>(copyFrom.ancestors()); + kind = copyFrom.kind(); } @SuppressWarnings("unchecked") @@ -53,30 +50,21 @@ protected B self() { return (B) this; } - public B addAncestor(String kind, long id) { - checkArgument(id != 0, "id must not be equal to zero"); - return addAncestor(new PathElement(kind, id)); + public B ancestors(PathElement ancestor) { + Preconditions.checkState(ancestors.size() < MAX_PATH, "path can have at most 100 elements"); + ancestors.add(ancestor); + return self(); } - public B addAncestor(String kind, String name) { - checkArgument(Strings.isNullOrEmpty(name) , "name must not be empty or null"); - checkArgument(name.length() <= 500, "name must not exceed 500 characters"); - return addAncestor(new PathElement(kind, name)); + public B ancestors(PathElement ancestor, PathElement... other) { + return ancestors(ImmutableList.builder().add(ancestor).add(other).build()); } - public B addAncestor(PathElement... ancestor) { - Preconditions.checkState(ancestors.size() + ancestor.length <= MAX_PATH, + public B ancestors(Iterable ancestors) { + ImmutableList list = ImmutableList.copyOf(ancestors); + Preconditions.checkState(this.ancestors.size() + list.size() < MAX_PATH, "path can have at most 100 elements"); - for (PathElement pathElement : ancestor) { - ancestors.add(pathElement); - } - return self(); - } - - public B addAncestors(Iterable ancestors) { - for (PathElement pathElement : ancestors) { - addAncestor(pathElement); - } + this.ancestors.addAll(list); return self(); } @@ -85,11 +73,6 @@ public B kind(String kind) { return self(); } - public B clearPath() { - ancestors.clear(); - return self(); - } - public B dataset(String dataset) { this.dataset = validateDataset(dataset); return self(); @@ -103,11 +86,10 @@ public B namespace(String namespace) { protected abstract BaseKey build(); } - BaseKey(String dataset, String namespace, ImmutableList ancestors, String kind) { + BaseKey(String dataset, String namespace, ImmutableList path) { this.dataset = dataset; this.namespace = namespace; - this.ancestors = ancestors; - this.kind = kind; + this.path = path; } /** @@ -128,19 +110,30 @@ public String namespace() { * Returns an immutable list with the key's ancestors. */ public List ancestors() { - return ancestors; + return path().subList(0, path().size() - 1); + } + + /** + * Returns an immutable list of the key's path (ancestors + self). + */ + public List path() { + return path; + } + + protected PathElement leaf() { + return path().get(path().size() - 1); } /** * Returns the key's kind. */ public String kind() { - return kind; + return leaf().kind(); } @Override public int hashCode() { - return Objects.hash(dataset(), namespace(), ancestors(), leaf()); + return Objects.hash(dataset(), namespace(), path()); } @Override @@ -154,8 +147,7 @@ public boolean equals(Object obj) { PartialKey other = (PartialKey) obj; return Objects.equals(dataset(), other.dataset()) && Objects.equals(namespace(), other.namespace()) - && Objects.equals(ancestors(), other.ancestors()) - && Objects.equals(leaf(), other.leaf()); + && Objects.equals(path(), other.path()); } @Override @@ -171,13 +163,9 @@ protected DatastoreV1.Key toPb() { if (partitionIdPb.hasDatasetId() || partitionIdPb.hasNamespace()) { keyPb.setPartitionId(partitionIdPb.build()); } - for (PathElement pathEntry : ancestors) { + for (PathElement pathEntry : path) { keyPb.addPathElement(pathEntry.toPb()); } - PathElement leaf = leaf(); - keyPb.addPathElement(leaf.toPb()); return keyPb.build(); } - - protected abstract PathElement leaf(); } diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index a9b7ae6a0965..113802a8ba5b 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -23,8 +23,6 @@ public final class Key extends PartialKey { private static final long serialVersionUID = 3160994559785491356L; - private final transient PathElement leaf; - public static final class Builder extends BaseKey.Builder { private String name; @@ -63,52 +61,50 @@ public Builder id(long id) { @Override public Key build() { + ImmutableList.Builder pathBuilder = + ImmutableList.builder().addAll(ancestors); if (id == null) { - return new Key(dataset, namespace, ImmutableList.copyOf(ancestors), kind, name); + pathBuilder.add(PathElement.of(kind, name)); + } else { + pathBuilder.add(PathElement.of(kind, id)); } - return new Key(dataset, namespace, ImmutableList.copyOf(ancestors), kind, id); + return new Key(dataset, namespace, pathBuilder.build()); } } - Key(String dataset, String namespace, ImmutableList ancestors, - String kind, String name) { - super(dataset, namespace, ancestors, kind); - leaf = new PathElement(kind, name); - } - - Key(String dataset, String namespace, ImmutableList ancestors, - String kind, long id) { - super(dataset, namespace, ancestors, kind); - leaf = new PathElement(kind, id); + Key(String dataset, String namespace, ImmutableList path) { + super(dataset, namespace, path); + Preconditions.checkArgument(nameOrId() != null); } public boolean hasId() { - return leaf.hasId(); + return leaf().hasId(); } /** * Returns the key's id or {@code null} if it has a name instead. */ public Long id() { - return leaf.id(); + return leaf().id(); } public boolean hasName() { - return leaf.hasName(); + return leaf().hasName(); } /** * Returns the key's name or {@code null} if it has an id instead. */ public String name() { - return leaf.name(); + return leaf().name(); } /** * Returns the key's id (as {@link Long}) or name (as {@link String}). + * Never {@code null}. */ public Object nameOrId() { - return leaf.nameOrId(); + return leaf().nameOrId(); } /** @@ -139,11 +135,6 @@ public static Key fromUrlSafe(String urlSafe) { } } - @Override - protected PathElement leaf() { - return leaf; - } - @Override protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { return fromPb(DatastoreV1.Key.parseFrom(bytesPb)); @@ -181,11 +172,11 @@ public static Builder builder(Key parent, String kind, long id) { private static void addParentToBuilder(Key parent, Builder builder) { builder.namespace(parent.namespace()); - builder.addAncestors(parent.ancestors()); + builder.ancestors(parent.ancestors()); if (parent.hasId()) { - builder.addAncestor(new PathElement(parent.kind(), parent.id())); + builder.ancestors(PathElement.of(parent.kind(), parent.id())); } else { - builder.addAncestor(new PathElement(parent.kind(), parent.name())); + builder.ancestors(PathElement.of(parent.kind(), parent.name())); } } } diff --git a/src/main/java/com/google/gcloud/datastore/KeyFactory.java b/src/main/java/com/google/gcloud/datastore/KeyFactory.java index f671e0f72da5..4984d71100fe 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyFactory.java +++ b/src/main/java/com/google/gcloud/datastore/KeyFactory.java @@ -18,21 +18,22 @@ public KeyFactory(DatastoreService service, String kind) { namespace(service.options().namespace()); } - @Override - protected PartialKey build() { - return new PartialKey(dataset, namespace, ImmutableList.copyOf(ancestors), kind); - } - public PartialKey newKey() { - return build(); + ImmutableList path = ImmutableList.builder() + .addAll(ancestors).add(PathElement.of(kind)).build(); + return new PartialKey(dataset, namespace, path); } public Key newKey(String name) { - return new Key(dataset, namespace, ImmutableList.copyOf(ancestors), kind, name); + ImmutableList path = ImmutableList.builder() + .addAll(ancestors).add(PathElement.of(kind, name)).build(); + return new Key(dataset, namespace, path); } public Key newKey(long id) { - return new Key(dataset, namespace, ImmutableList.copyOf(ancestors), kind, id); + ImmutableList path = ImmutableList.builder() + .addAll(ancestors).add(PathElement.of(kind, id)).build(); + return new Key(dataset, namespace, path); } /** @@ -40,6 +41,11 @@ public Key newKey(long id) { * @throws DatastoreServiceException if allocation failed. */ public Key allocateId() { - return service.allocateId(build()); + return service.allocateId(newKey()); + } + + @Override + protected PartialKey build() { + return newKey(); } } diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index 7630f3ebf275..302abbd065c8 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -1,5 +1,6 @@ package com.google.gcloud.datastore; +import com.google.api.client.repackaged.com.google.common.base.Preconditions; import com.google.api.services.datastore.DatastoreV1; import com.google.common.collect.ImmutableList; import com.google.protobuf.InvalidProtocolBufferException; @@ -27,25 +28,23 @@ private Builder(PartialKey copyFrom) { @Override public PartialKey build() { - return new PartialKey(dataset, namespace, ImmutableList.copyOf(ancestors), kind); + ImmutableList path = ImmutableList.builder() + .addAll(ancestors).add(PathElement.of(kind)).build(); + return new PartialKey(dataset, namespace, path); } } - PartialKey(String dataset, String namespace, ImmutableList ancestors, String kind) { - super(dataset, namespace, ancestors, kind); + PartialKey(String dataset, String namespace, ImmutableList path) { + super(dataset, namespace, path); } public Key newKey(String name) { - return new Key(dataset(), namespace(), ImmutableList.copyOf(ancestors()), kind(), name); + return Key.builder(dataset(), kind(), name) + .namespace(namespace()).ancestors(ancestors()).build(); } public Key newKey(long id) { - return new Key(dataset(), namespace(), ImmutableList.copyOf(ancestors()), kind(), id); - } - - @Override - protected PathElement leaf() { - return new PathElement(kind()); + return Key.builder(dataset(), kind(), id).namespace(namespace()).ancestors(ancestors()).build(); } @Override @@ -66,21 +65,17 @@ static PartialKey fromPb(DatastoreV1.Key keyPb) { } } List pathElementsPb = keyPb.getPathElementList(); - if (pathElementsPb.isEmpty()) { - return new PartialKey(dataset, namespace, ImmutableList.of(), null); - } + Preconditions.checkArgument(pathElementsPb.size() > 0, "Path must not be empty"); ImmutableList.Builder pathBuilder = ImmutableList.builder(); - for (int i = 0; i < pathElementsPb.size() - 1; i++) { - pathBuilder.add(PathElement.fromPb(pathElementsPb.get(i))); + for (DatastoreV1.Key.PathElement pathElementPb : pathElementsPb) { + pathBuilder.add(PathElement.fromPb(pathElementPb)); } - DatastoreV1.Key.PathElement leaf = pathElementsPb.get(pathElementsPb.size() - 1); - String kind = leaf.getKind(); - if (leaf.hasId()) { - return new Key(dataset, namespace, pathBuilder.build(), kind, leaf.getId()); - } else if (leaf.hasName()) { - return new Key(dataset, namespace, pathBuilder.build(), kind, leaf.getName()); + ImmutableList path = pathBuilder.build(); + PathElement leaf = path.get(path.size() - 1); + if (leaf.nameOrId() != null) { + return new Key(dataset, namespace, path); } - return new PartialKey(dataset, namespace, pathBuilder.build(), kind); + return new PartialKey(dataset, namespace, path); } public static Builder builder(String dataset, String kind) { @@ -92,14 +87,6 @@ public static Builder builder(PartialKey copyFrom) { } public static Builder builder(Key parent, String kind) { - Builder builder = new Builder(parent.dataset(), kind) - .namespace(parent.namespace()) - .addAncestors(parent.ancestors()); - if (parent.hasId()) { - builder.addAncestor(new PathElement(parent.kind(), parent.id())); - } else { - builder.addAncestor(new PathElement(parent.kind(), parent.name())); - } - return builder; + return builder(parent.dataset(), kind).namespace(parent.namespace()).ancestors(parent.path()); } } diff --git a/src/main/java/com/google/gcloud/datastore/PathElement.java b/src/main/java/com/google/gcloud/datastore/PathElement.java index 34dcc06de4e6..82a4545faf8e 100644 --- a/src/main/java/com/google/gcloud/datastore/PathElement.java +++ b/src/main/java/com/google/gcloud/datastore/PathElement.java @@ -1,6 +1,10 @@ package com.google.gcloud.datastore; +import static com.google.api.client.repackaged.com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.api.services.datastore.DatastoreV1; +import com.google.common.base.Strings; import com.google.protobuf.InvalidProtocolBufferException; import java.util.Objects; @@ -16,20 +20,10 @@ public final class PathElement extends Serializable private final transient Long id; private final transient String name; - PathElement(String kind) { - this(kind, null); - } - - public PathElement(String kind, long id) { - this.kind = kind; - this.id = id; - name = null; - } - - public PathElement(String kind, String name) { - this.kind = kind; + private PathElement(String kind, String name, Long id) { + this.kind = checkNotNull(kind); this.name = name; - id = null; + this.id = id; } public String kind() { @@ -95,10 +89,25 @@ protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { static PathElement fromPb(DatastoreV1.Key.PathElement pathElementPb) { String kind = pathElementPb.getKind(); if (pathElementPb.hasId()) { - return new PathElement(kind, pathElementPb.getId()); + return PathElement.of(kind, pathElementPb.getId()); } else if (pathElementPb.hasName()) { - return new PathElement(kind, pathElementPb.getName()); + return PathElement.of(kind, pathElementPb.getName()); } - return new PathElement(kind); + return PathElement.of(kind); + } + + static PathElement of(String kind) { + return new PathElement(kind, null, null); + } + + public static PathElement of(String kind, String name) { + checkArgument(!Strings.isNullOrEmpty(name) , "name must not be empty or null"); + checkArgument(name.length() <= 500, "name must not exceed 500 characters"); + return new PathElement(kind, name, null); + } + + public static PathElement of(String kind, long id) { + checkArgument(id != 0, "id must not be equal to zero"); + return new PathElement(kind, null, id); } } diff --git a/src/test/java/com/google/gcloud/ExceptionHandlerTest.java b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java index 10bd594b46a2..42905c85a08a 100644 --- a/src/test/java/com/google/gcloud/ExceptionHandlerTest.java +++ b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java @@ -12,7 +12,6 @@ import java.io.IOException; import java.nio.channels.ClosedByInterruptException; import java.util.concurrent.Callable; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; /** diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index 864d60f6160b..a1d021c3739d 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -241,6 +241,7 @@ public void testNewBatchWriter() { Entity entity2 = Entity.builder(ENTITY2).clear().setNull("bla").build(); Entity entity4 = Entity.builder(KEY4).set("value", StringValue.of("value")).build(); Entity entity5 = Entity.builder(KEY5).set("value", "value").build(); + batchWriter.add(entity4, entity5); batchWriter.put(ENTITY3, entity1, entity2); batchWriter.submit(); @@ -312,9 +313,9 @@ public void testRunGqlQueryNoCasting() throws DatastoreException { Type.PROJECTION, "select __key__ from " + KIND1).build(); QueryResult keyProjectionResult = datastore.run(keyProjectionQuery); assertTrue(keyProjectionResult.hasNext()); - ProjectionEntity p = keyProjectionResult.next(); - assertEquals(KEY1, p.key()); - assertTrue(p.properties().isEmpty()); + ProjectionEntity projectionEntity = keyProjectionResult.next(); + assertEquals(KEY1, projectionEntity.key()); + assertTrue(projectionEntity.properties().isEmpty()); assertFalse(keyProjectionResult.hasNext()); GqlQuery projectionQuery = GqlQuery.builder( @@ -338,7 +339,7 @@ public void testRunGqlQueryNoCasting() throws DatastoreException { QueryResult projectionResult = datastore.run(projectionQuery); assertTrue(projectionResult.hasNext()); - ProjectionEntity projectionEntity = projectionResult.next(); + projectionEntity = projectionResult.next(); assertEquals("str", projectionEntity.getString("str")); assertEquals(DATE_TIME_VALUE.get(), projectionEntity.getDateTime("date")); assertEquals(DATE_TIME_VALUE.get().timestampMicroseconds(), @@ -454,7 +455,7 @@ public void testAllocateId() { public void testAllocateIdArray() { KeyFactory keyFactory = new KeyFactory(datastore, KIND1); PartialKey partialKey1 = keyFactory.newKey(); - PartialKey partialKey2 = keyFactory.kind(KIND2).addAncestor(KIND1, 10).newKey(); + PartialKey partialKey2 = keyFactory.kind(KIND2).ancestors(PathElement.of(KIND1, 10)).newKey(); Key key3 = keyFactory.newKey("name"); Key key4 = keyFactory.newKey(1); Iterator result = @@ -481,14 +482,14 @@ public void testGet() { entity = datastore.get(KEY1); assertEquals(ENTITY1, entity); StringValue value1 = entity.getValue("str"); - BooleanValue value2 = entity.getValue("bool"); - ListValue value3 = entity.getValue("list"); - DateTimeValue value4 = entity.getValue("date"); - PartialEntity value5 = entity.getEntity("partial1"); assertEquals(STR_VALUE, value1); + BooleanValue value2 = entity.getValue("bool"); assertEquals(BOOL_VALUE, value2); + ListValue value3 = entity.getValue("list"); assertEquals(LIST_VALUE2, value3); + DateTimeValue value4 = entity.getValue("date"); assertEquals(DATE_TIME_VALUE, value4); + PartialEntity value5 = entity.getEntity("partial1"); assertEquals(PARTIAL_ENTITY1, value5); assertEquals(5, entity.names().size()); assertFalse(entity.contains("bla")); @@ -511,7 +512,7 @@ public void testGetArray() { Entity partial2 = (Entity) entity3.getEntity("partial2"); assertEquals(partial1, PARTIAL_ENTITY2); assertEquals(partial2, ENTITY2); - assertEquals(Value.Type.BOOLEAN, entity3.type("bool")); + assertEquals(Value.Type.BOOLEAN, entity3.getValue("bool").type()); assertEquals(6, entity3.names().size()); assertFalse(entity3.contains("bla")); try { diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 706c247c6623..636ab831c8ac 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -23,10 +23,10 @@ public class SerializationTest { private static final PartialKey INCOMPLETE_KEY1 = - PartialKey.builder("ds", "k").addAncestor("p", 1).build(); + PartialKey.builder("ds", "k").ancestors(PathElement.of("p", 1)).build(); private static final Key KEY1 = Key.builder("ds", "k", "n").build(); private static final PartialKey INCOMPLETE_KEY2 = - PartialKey.builder(KEY1, "v").addAncestor("p", 1).build(); + PartialKey.builder(KEY1, "v").ancestors(PathElement.of("p", 1)).build(); private static final Key KEY2 = Key.builder(KEY1, "v", 2).build(); private static final DateTime DATE_TIME1 = DateTime.now(); private static final Blob BLOB1 = Blob.copyFrom(UTF_8.encode("hello world")); From 01ab2c08244fc60af8995586dba6d2c9b160c884 Mon Sep 17 00:00:00 2001 From: aozarov Date: Fri, 19 Dec 2014 16:30:25 -0800 Subject: [PATCH 069/771] Adding DatastoreHelper --- .../com/google/gcloud/datastore/BaseKey.java | 6 +- .../gcloud/datastore/DatastoreHelper.java | 131 ++++++++++++++++++ .../gcloud/datastore/DatastoreService.java | 4 +- .../google/gcloud/datastore/KeyFactory.java | 4 +- .../google/gcloud/datastore/PathElement.java | 2 +- .../google/gcloud/datastore/package-info.java | 2 +- .../datastore/DatastoreServiceTest.java | 50 +++---- 7 files changed, 160 insertions(+), 39 deletions(-) create mode 100644 src/main/java/com/google/gcloud/datastore/DatastoreHelper.java diff --git a/src/main/java/com/google/gcloud/datastore/BaseKey.java b/src/main/java/com/google/gcloud/datastore/BaseKey.java index f9d961034941..ea9f600aff7e 100644 --- a/src/main/java/com/google/gcloud/datastore/BaseKey.java +++ b/src/main/java/com/google/gcloud/datastore/BaseKey.java @@ -32,9 +32,13 @@ abstract static class Builder> { private static final int MAX_PATH = 100; - public Builder(String dataset, String kind) { + public Builder(String dataset) { this.dataset = validateDataset(dataset); ancestors = new LinkedList<>(); + } + + public Builder(String dataset, String kind) { + this(dataset); this.kind = validateKind(kind); } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java b/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java new file mode 100644 index 000000000000..7fe4aeda0d96 --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java @@ -0,0 +1,131 @@ +package com.google.gcloud.datastore; + +import com.google.common.collect.Maps; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Adds some functionality to DatastoreService that should + * be provided statically to the interface (Java 8). + * + */ +public class DatastoreHelper implements DatastoreService { + + private final DatastoreService delegate; + + private DatastoreHelper(DatastoreService delegate) { + this.delegate = delegate; + } + + @Override + public Entity get(Key key) { + return delegate.get(key); + } + + @Override + public Iterator get(Key key, Key... others) { + return delegate.get(key, others); + } + + @Override + public QueryResult run(Query query) { + return delegate.run(query); + } + + @Override + public DatastoreServiceOptions options() { + return delegate.options(); + } + + @Override + public Transaction newTransaction(TransactionOption... options) { + return delegate.newTransaction(options); + } + + @Override + public BatchWriter newBatchWriter(BatchWriteOption... options) { + return delegate.newBatchWriter(options); + } + + @Override + public Key allocateId(PartialKey key) { + return delegate.allocateId(key); + } + + @Override + public Iterator allocateId(PartialKey key, PartialKey... others) { + return delegate.allocateId(key, others); + } + + @Override + public void add(Entity... entity) { + delegate.add(entity); + } + + @Override + public void update(Entity... entity) { + delegate.update(entity); + } + + @Override + public void put(Entity... entity) { + delegate.put(entity); + } + + @Override + public void delete(Key... key) { + delegate.delete(key); + } + + /** + * Returns a new KeyFactory for this service + */ + public KeyFactory newKeyFactory() { + return new KeyFactory(this); + } + + /** + * Returns a list with a value for each given key (ordered by input). + * A {@code null} would be returned for non-existing keys. + */ + public List fetch(Key key, Key... others) { + Iterator entities = delegate.get(key, others); + Map map = Maps.newHashMapWithExpectedSize(1 + others.length); + while (entities.hasNext()) { + Entity entity = entities.next(); + map.put(entity.key(), entity); + } + List list = new ArrayList<>(1 + others.length); + list.add(map.get(key)); + for (Key other : others) { + list.add(map.get(other)); + } + return list; + } + + public interface RunInTransaction { + void run(DatastoreReaderWriter readerWriter); + } + + public void runInTransaction(RunInTransaction runFor, TransactionOption... options) { + Transaction transaction = newTransaction(options); + try { + runFor.run(transaction); + transaction.commit(); + } finally { + if (transaction.active()) { + transaction.rollback(); + } + } + } + + public static DatastoreHelper createFor(DatastoreService datastoreService) { + if (datastoreService instanceof DatastoreHelper) { + return (DatastoreHelper) datastoreService; + } + return new DatastoreHelper(datastoreService); + } +} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index 45076277f701..f0f84191b18e 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -17,13 +17,13 @@ public interface DatastoreService extends DatastoreReaderWriter { * * @throws DatastoreServiceExcepiton upon failure */ - Transaction newTransaction(TransactionOption... transactionOption); + Transaction newTransaction(TransactionOption... options); /** * Returns a new Batch writer for processing multiple write operations * in one request. */ - BatchWriter newBatchWriter(BatchWriteOption... batchWriteOption); + BatchWriter newBatchWriter(BatchWriteOption... options); /** * Allocate a unique id for the given key. diff --git a/src/main/java/com/google/gcloud/datastore/KeyFactory.java b/src/main/java/com/google/gcloud/datastore/KeyFactory.java index 4984d71100fe..08d854404a3b 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyFactory.java +++ b/src/main/java/com/google/gcloud/datastore/KeyFactory.java @@ -12,8 +12,8 @@ public final class KeyFactory extends BaseKey.Builder { private final DatastoreService service; - public KeyFactory(DatastoreService service, String kind) { - super(checkNotNull(service).options().dataset(), kind); + public KeyFactory(DatastoreService service) { + super(checkNotNull(service).options().dataset()); this.service = service; namespace(service.options().namespace()); } diff --git a/src/main/java/com/google/gcloud/datastore/PathElement.java b/src/main/java/com/google/gcloud/datastore/PathElement.java index 82a4545faf8e..5652359f2818 100644 --- a/src/main/java/com/google/gcloud/datastore/PathElement.java +++ b/src/main/java/com/google/gcloud/datastore/PathElement.java @@ -21,7 +21,7 @@ public final class PathElement extends Serializable private final transient String name; private PathElement(String kind, String name, Long id) { - this.kind = checkNotNull(kind); + this.kind = checkNotNull(kind, "kind must not be null"); this.name = name; this.id = id; } diff --git a/src/main/java/com/google/gcloud/datastore/package-info.java b/src/main/java/com/google/gcloud/datastore/package-info.java index ea3e92b7386b..88933a598ca9 100644 --- a/src/main/java/com/google/gcloud/datastore/package-info.java +++ b/src/main/java/com/google/gcloud/datastore/package-info.java @@ -5,7 +5,7 @@ *
 {@code
  * DatastoreServiceOptions options = DatastoreServiceOptions.builder().dataset(DATASET).build();
  * DatastoreService datastore = DatastoreServiceFactory.getDefault(options);
- * KeyFactory keyFactory = new KeyFactory(datastore, kind);
+ * KeyFactory keyFactory = new KeyFactory(datastore).kind(kind);
  * Key key = keyFactory.newKey(keyName);
  * Entity entity = datastore.get(key);
  * if (entity == null) {
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
index a1d021c3739d..f0df27a88230 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
@@ -11,7 +11,6 @@
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.api.services.datastore.client.Datastore;
 import com.google.api.services.datastore.client.DatastoreException;
-import com.google.common.collect.Maps;
 import com.google.gcloud.datastore.Query.Type;
 import com.google.gcloud.datastore.StructuredQuery.OrderBy;
 import com.google.gcloud.datastore.StructuredQuery.Projection;
@@ -21,7 +20,6 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -68,6 +66,7 @@ public class DatastoreServiceTest {
 
   private DatastoreServiceOptions options;
   private DatastoreService datastore;
+  private DatastoreHelper helper;
 
   @Before
   public void setUp() {
@@ -83,6 +82,7 @@ public void setUp() {
         .host("http://localhost:8080")
         .build();
     datastore = DatastoreServiceFactory.getDefault(options);
+    helper = DatastoreHelper.createFor(datastore);
     // Prepare data for testing
     datastore.delete(KEY1, KEY2, KEY3, KEY4, KEY5);
     datastore.add(ENTITY1, ENTITY2);
@@ -105,7 +105,7 @@ public void testNewTransactionCommit() {
     transaction.delete(KEY1);
     transaction.commit();
 
-    List list = fetch(KEY1, KEY2, KEY3);
+    List list = helper.fetch(KEY1, KEY2, KEY3);
     assertNull(list.get(0));
     assertEquals(entity2, list.get(1));
     assertEquals(ENTITY3, list.get(2));
@@ -197,7 +197,7 @@ public void testNewTransactionRollback() {
 
     verifyNotUsable(transaction);
 
-    List list = fetch(KEY1, KEY2, KEY3);
+    List list = helper.fetch(KEY1, KEY2, KEY3);
     assertEquals(ENTITY1, list.get(0));
     assertEquals(ENTITY2, list.get(1));
     assertNull(list.get(2));
@@ -245,7 +245,8 @@ public void testNewBatchWriter() {
     batchWriter.add(entity4, entity5);
     batchWriter.put(ENTITY3, entity1, entity2);
     batchWriter.submit();
-    Iterator entities = fetch(KEY1, KEY2, KEY3, entity4.key(), entity5.key()).iterator();
+    Iterator entities =
+        helper.fetch(KEY1, KEY2, KEY3, entity4.key(), entity5.key()).iterator();
     assertEquals(entity1, entities.next());
     assertEquals(entity2, entities.next());
     assertEquals(ENTITY3, entities.next());
@@ -265,7 +266,7 @@ public void testNewBatchWriter() {
     batchWriter.delete(entity4.key(), entity5.key());
     batchWriter.update(ENTITY1, ENTITY2, ENTITY3);
     batchWriter.submit();
-    entities = fetch(KEY1, KEY2, KEY3, entity4.key(), entity5.key()).iterator();
+    entities = helper.fetch(KEY1, KEY2, KEY3, entity4.key(), entity5.key()).iterator();
     assertEquals(ENTITY1, entities.next());
     assertEquals(ENTITY2, entities.next());
     assertEquals(ENTITY3, entities.next());
@@ -431,7 +432,7 @@ public void testRunStructuredQuery() throws DatastoreException {
 
   @Test
   public void testAllocateId() {
-    KeyFactory keyFactory = new KeyFactory(datastore, KIND1);
+    KeyFactory keyFactory = helper.newKeyFactory().kind(KIND1);
     PartialKey pk1 = keyFactory.newKey();
     Key key1 = keyFactory.allocateId();
     assertEquals(key1.dataset(), pk1.dataset());
@@ -453,7 +454,7 @@ public void testAllocateId() {
 
   @Test
   public void testAllocateIdArray() {
-    KeyFactory keyFactory = new KeyFactory(datastore, KIND1);
+    KeyFactory keyFactory = helper.newKeyFactory().kind(KIND1);
     PartialKey partialKey1 = keyFactory.newKey();
     PartialKey partialKey2 = keyFactory.kind(KIND2).ancestors(PathElement.of(KIND1, 10)).newKey();
     Key key3 = keyFactory.newKey("name");
@@ -499,7 +500,7 @@ public void testGet() {
   public void testGetArray() {
     datastore.put(ENTITY3);
     Iterator result =
-        fetch(KEY1, Key.builder(KEY1).name("bla").build(), KEY2, KEY3).iterator();
+        helper.fetch(KEY1, Key.builder(KEY1).name("bla").build(), KEY2, KEY3).iterator();
     assertEquals(ENTITY1, result.next());
     assertNull(result.next());
     assertEquals(ENTITY2, result.next());
@@ -525,24 +526,9 @@ public void testGetArray() {
     // TODO(ozarov): construct a test to verify more results
   }
 
-  public List fetch(Key key, Key... others) {
-    Iterator entities = datastore.get(key, others);
-    Map map = Maps.newHashMapWithExpectedSize(1 + others.length);
-    while (entities.hasNext()) {
-      Entity entity = entities.next();
-      map.put(entity.key(), entity);
-    }
-    List list = new ArrayList<>(1 + others.length);
-    list.add(map.get(key));
-    for (Key other : others) {
-      list.add(map.get(other));
-    }
-    return list;
-  }
-
   @Test
   public void testAdd() {
-    List keys = fetch(ENTITY1.key(), ENTITY3.key());
+    List keys = helper.fetch(ENTITY1.key(), ENTITY3.key());
     assertEquals(ENTITY1, keys.get(0));
     assertNull(keys.get(1));
     assertEquals(2, keys.size());
@@ -559,7 +545,7 @@ public void testAdd() {
 
   @Test
   public void testUpdate() {
-    List keys = fetch(ENTITY1.key(), ENTITY3.key());
+    List keys = helper.fetch(ENTITY1.key(), ENTITY3.key());
     assertEquals(ENTITY1, keys.get(0));
     assertNull(keys.get(1));
     assertEquals(2, keys.size());
@@ -580,7 +566,7 @@ public void testUpdate() {
 
   @Test
   public void testPut() {
-    Iterator keys = fetch(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()).iterator();
+    Iterator keys = helper.fetch(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()).iterator();
     assertEquals(ENTITY1, keys.next());
     assertEquals(ENTITY2, keys.next());
     assertNull(keys.next());
@@ -589,7 +575,7 @@ public void testPut() {
     Entity entity2 = Entity.builder(ENTITY2).clear().set("bla", new NullValue()).build();
     assertNotEquals(ENTITY2, entity2);
     datastore.put(ENTITY3, ENTITY1, entity2);
-    keys = fetch(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()).iterator();
+    keys = helper.fetch(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()).iterator();
     assertEquals(ENTITY1, keys.next());
     assertEquals(entity2, keys.next());
     assertEquals(ENTITY3, keys.next());
@@ -598,13 +584,13 @@ public void testPut() {
 
   @Test
   public void testDelete() {
-    Iterator keys = fetch(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()).iterator();
+    Iterator keys = helper.fetch(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()).iterator();
     assertEquals(ENTITY1, keys.next());
     assertEquals(ENTITY2, keys.next());
     assertNull(keys.next());
     assertFalse(keys.hasNext());
     datastore.delete(ENTITY1.key(), ENTITY2.key(), ENTITY3.key());
-    keys = fetch(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()).iterator();
+    keys = helper.fetch(ENTITY1.key(), ENTITY2.key(), ENTITY3.key()).iterator();
     assertNull(keys.next());
     assertNull(keys.next());
     assertNull(keys.next());
@@ -613,10 +599,10 @@ public void testDelete() {
 
   @Test
   public void testKeyFactory() {
-    KeyFactory keyFactory = new KeyFactory(datastore, KIND1);
+    KeyFactory keyFactory = new KeyFactory(datastore).kind(KIND1);
     assertEquals(PARTIAL_KEY1, keyFactory.newKey());
     assertEquals(PartialKey.builder(PARTIAL_KEY1).kind(KIND2).build(),
-        new KeyFactory(datastore, KIND2).newKey());
+        new KeyFactory(datastore).kind(KIND2).newKey());
     assertEquals(KEY1, keyFactory.newKey("name"));
     assertEquals(Key.builder(KEY1).id(2).build(), keyFactory.newKey(2));
   }

From e6250dccff620e87c7a181e02f4622dfb4f666bc Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Sat, 20 Dec 2014 23:53:24 -0800
Subject: [PATCH 070/771] Incorporate retry-helper to datastore service.

---
 .../java/com/google/gcloud/RetryHelper.java   |   5 +-
 .../java/com/google/gcloud/RetryParams.java   |  66 ++---
 .../com/google/gcloud/ServiceOptions.java     |  25 +-
 .../gcloud/datastore/BatchWriterImpl.java     |   2 +-
 .../datastore/DatastoreServiceException.java  |  14 +-
 .../datastore/DatastoreServiceImpl.java       | 134 ++++++---
 .../gcloud/datastore/QueryResultImpl.java     |  19 +-
 .../com/google/gcloud/RetryHelperTest.java    | 256 ++++++++++++++++++
 .../datastore/DatastoreServiceTest.java       |   4 +-
 9 files changed, 440 insertions(+), 85 deletions(-)
 create mode 100644 src/test/java/com/google/gcloud/RetryHelperTest.java

diff --git a/src/main/java/com/google/gcloud/RetryHelper.java b/src/main/java/com/google/gcloud/RetryHelper.java
index ac4489684e3e..ed0fcc1dab85 100644
--- a/src/main/java/com/google/gcloud/RetryHelper.java
+++ b/src/main/java/com/google/gcloud/RetryHelper.java
@@ -1,6 +1,7 @@
 package com.google.gcloud;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.Math.max;
 import static java.lang.Math.min;
 import static java.lang.Math.pow;
 import static java.lang.Math.random;
@@ -68,7 +69,7 @@ public static final class RetryInterruptedException extends RetryHelperException
     /**
      * Sets the caller thread interrupt flag and throws {@code RetryInteruptedException}.
      */
-    static void propagate() throws RetryInterruptedException {
+    public static void propagate() throws RetryInterruptedException {
       Thread.currentThread().interrupt();
       throw new RetryInterruptedException();
     }
@@ -200,7 +201,7 @@ static long getSleepDuration(RetryParams retryParams, int attemptsSoFar) {
 
   private static long getExponentialValue(long initialDelay, double backoffFactor, long maxDelay,
       int attemptsSoFar) {
-    return (long) min(maxDelay, pow(backoffFactor, min(1, attemptsSoFar) - 1) * initialDelay);
+    return (long) min(maxDelay, pow(backoffFactor, max(1, attemptsSoFar) - 1) * initialDelay);
   }
 
   public static  V runWithRetries(Callable callable) throws RetryHelperException {
diff --git a/src/main/java/com/google/gcloud/RetryParams.java b/src/main/java/com/google/gcloud/RetryParams.java
index cd500cd4149f..4da4c8399c8e 100644
--- a/src/main/java/com/google/gcloud/RetryParams.java
+++ b/src/main/java/com/google/gcloud/RetryParams.java
@@ -47,37 +47,10 @@ public final class RetryParams implements Serializable {
   private final long totalRetryPeriodMillis;
 
   private static final RetryParams DEFAULT_INSTANCE = new RetryParams(new Builder());
+  private static final RetryParams NO_RETRIES =
+      builder().retryMaxAttempts(1).retryMinAttempts(1).build();
 
 
-  /**
-   * Create a new RetryParams with the parameters from a {@link RetryParams.Builder}
-   *
-   * @param builder the parameters to use to construct the RetryParams object
-   */
-  private RetryParams(Builder builder) {
-    retryMinAttempts = builder.retryMinAttempts;
-    retryMaxAttempts = builder.retryMaxAttempts;
-    initialRetryDelayMillis = builder.initialRetryDelayMillis;
-    maxRetryDelayMillis = builder.maxRetryDelayMillis;
-    retryDelayBackoffFactor = builder.retryDelayBackoffFactor;
-    totalRetryPeriodMillis = builder.totalRetryPeriodMillis;
-    checkArgument(retryMinAttempts >= 0, "retryMinAttempts must not be negative");
-    checkArgument(retryMaxAttempts >= retryMinAttempts,
-        "retryMaxAttempts must not be smaller than retryMinAttempts");
-    checkArgument(initialRetryDelayMillis >= 0, "initialRetryDelayMillis must not be negative");
-    checkArgument(maxRetryDelayMillis >= initialRetryDelayMillis,
-        "maxRetryDelayMillis must not be smaller than initialRetryDelayMillis");
-    checkArgument(retryDelayBackoffFactor >= 0, "retryDelayBackoffFactor must not be negative");
-    checkArgument(totalRetryPeriodMillis >= 0, "totalRetryPeriodMillis must not be negative");
-  }
-
-  /**
-   * Returns an instance with the default parameters.
-   */
-  public static RetryParams getDefaultInstance() {
-    return DEFAULT_INSTANCE;
-  }
-
   /**
    * RetryParams builder.
    */
@@ -188,6 +161,39 @@ public RetryParams build() {
     }
   }
 
+  /**
+   * Create a new RetryParams with the parameters from a {@link RetryParams.Builder}
+   *
+   * @param builder the parameters to use to construct the RetryParams object
+   */
+  private RetryParams(Builder builder) {
+    retryMinAttempts = builder.retryMinAttempts;
+    retryMaxAttempts = builder.retryMaxAttempts;
+    initialRetryDelayMillis = builder.initialRetryDelayMillis;
+    maxRetryDelayMillis = builder.maxRetryDelayMillis;
+    retryDelayBackoffFactor = builder.retryDelayBackoffFactor;
+    totalRetryPeriodMillis = builder.totalRetryPeriodMillis;
+    checkArgument(retryMinAttempts >= 0, "retryMinAttempts must not be negative");
+    checkArgument(retryMaxAttempts >= retryMinAttempts,
+        "retryMaxAttempts must not be smaller than retryMinAttempts");
+    checkArgument(initialRetryDelayMillis >= 0, "initialRetryDelayMillis must not be negative");
+    checkArgument(maxRetryDelayMillis >= initialRetryDelayMillis,
+        "maxRetryDelayMillis must not be smaller than initialRetryDelayMillis");
+    checkArgument(retryDelayBackoffFactor >= 0, "retryDelayBackoffFactor must not be negative");
+    checkArgument(totalRetryPeriodMillis >= 0, "totalRetryPeriodMillis must not be negative");
+  }
+
+  /**
+   * Returns an instance with the default parameters.
+   */
+  public static RetryParams getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  public static RetryParams noRetries() {
+    return NO_RETRIES;
+  }
+
   /**
    * Returns the retryMinAttempts.
    */
@@ -230,8 +236,6 @@ public long getTotalRetryPeriodMillis() {
     return totalRetryPeriodMillis;
   }
 
-
-
   @Override
   public int hashCode() {
     return Objects.hash(retryMinAttempts, retryMaxAttempts, initialRetryDelayMillis,
diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java
index 24510adf7688..0153d364e365 100644
--- a/src/main/java/com/google/gcloud/ServiceOptions.java
+++ b/src/main/java/com/google/gcloud/ServiceOptions.java
@@ -17,11 +17,13 @@ public abstract class ServiceOptions {
   private final String host;
   private final HttpTransport httpTransport;
   private final AuthConfig authConfig;
+  private final RetryParams retryParams;
 
   protected ServiceOptions(Builder builder) {
     host = firstNonNull(builder.host, DEFAULT_HOST);
     httpTransport = firstNonNull(builder.httpTransport, defaultHttpTransport());
     authConfig = firstNonNull(builder.authConfig, defaultAuthConfig());
+    retryParams = builder.retryParams;
   }
 
   private static HttpTransport defaultHttpTransport() {
@@ -69,6 +71,7 @@ protected abstract static class Builder> {
     private String host;
     private HttpTransport httpTransport;
     private AuthConfig authConfig;
+    private RetryParams retryParams;
 
     protected Builder() {}
 
@@ -76,26 +79,34 @@ protected Builder(ServiceOptions options) {
       host = options.host;
       httpTransport = options.httpTransport;
       authConfig = options.authConfig;
+      retryParams = options.retryParams;
     }
 
     protected abstract ServiceOptions build();
 
     @SuppressWarnings("unchecked")
+    protected B self() {
+      return (B) this;
+    }
+
     public B host(String host) {
       this.host = host;
-      return (B) this;
+      return self();
     }
 
-    @SuppressWarnings("unchecked")
     public B httpTransport(HttpTransport httpTransport) {
       this.httpTransport = httpTransport;
-      return (B) this;
+      return self();
     }
 
-    @SuppressWarnings("unchecked")
     public B authConfig(AuthConfig authConfig) {
       this.authConfig = authConfig;
-      return (B) this;
+      return self();
+    }
+
+    public B retryParams(RetryParams retryParams) {
+      this.retryParams = retryParams;
+      return self();
     }
   }
 
@@ -113,6 +124,10 @@ public AuthConfig authConfig() {
     return authConfig;
   }
 
+  public RetryParams retryParams() {
+    return retryParams;
+  }
+
   protected HttpRequestInitializer httpRequestInitializer() {
     return authConfig().httpRequestInitializer(httpTransport, scopes());
   }
diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
index 9f760faee971..3589fe3fec24 100644
--- a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
@@ -119,7 +119,7 @@ public void submit() {
     }
     DatastoreV1.CommitRequest.Builder requestPb = newCommitRequest();
     requestPb.setMutation(mutationPb);
-    datastore.commitMutation(requestPb);
+    datastore.commit(requestPb.build());
     active = false;
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java
index 7e5ec384c65c..50c745b7679b 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java
@@ -3,6 +3,8 @@
 import com.google.api.services.datastore.client.DatastoreException;
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableMap;
+import com.google.gcloud.RetryHelper;
+import com.google.gcloud.RetryHelper.RetryHelperException;
 
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -37,7 +39,7 @@ public enum Code {
 
     Code(boolean isTransient, String msg) {
       this.isTransient = isTransient;
-      this.defaultMessage = msg;
+      defaultMessage = msg;
     }
 
     /**
@@ -77,6 +79,16 @@ public Code code() {
     return code;
   }
 
+  static DatastoreServiceException translateAndThrow(RetryHelperException ex) {
+    if (ex.getCause() instanceof DatastoreException) {
+      return translateAndThrow((DatastoreException) ex.getCause());
+    }
+    if (ex instanceof RetryHelper.RetryInterruptedException) {
+      RetryHelper.RetryInterruptedException.propagate();
+    }
+    throw new DatastoreServiceException(Code.UNKNOWN, ex.getMessage(), ex);
+  }
+
   /**
    * Translate DatastoreException to DatastoreServiceException based on their
    * HTTP error codes. This method will always throw a new DatastoreServiceException.
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
index 91bfa0137b57..875660de74b8 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
@@ -3,26 +3,55 @@
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.api.services.datastore.client.Datastore;
 import com.google.api.services.datastore.client.DatastoreException;
+import com.google.common.base.MoreObjects;
 import com.google.common.collect.AbstractIterator;
+import com.google.gcloud.ExceptionHandler;
+import com.google.gcloud.RetryHelper;
+import com.google.gcloud.RetryHelper.RetryHelperException;
+import com.google.gcloud.RetryParams;
 import com.google.protobuf.ByteString;
 
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.concurrent.Callable;
 
 
 final class DatastoreServiceImpl implements DatastoreService {
 
   static final Key[] EMPTY_KEY_ARRAY = {};
   static final PartialKey[] EMPTY_PARTIAL_KEY_ARRAY = {};
+  private static final ExceptionHandler.Interceptor EXCEPTION_HANDLER_INTERCEPTOR =
+      new ExceptionHandler.Interceptor() {
+
+        private static final long serialVersionUID = 6911242958397733203L;
+
+        @Override
+        public boolean shouldRetry(Exception exception, boolean shouldRetry) {
+          return shouldRetry;
+        }
+
+        @Override
+        public Boolean shouldRetry(Exception exception) {
+          if (exception instanceof DatastoreServiceException) {
+            return ((DatastoreServiceException) exception).code().isTransient();
+          }
+          return null;
+        }
+      };
+  private static final ExceptionHandler EXCEPTION_HANDLER = ExceptionHandler.builder()
+      .abortOn(RuntimeException.class, DatastoreException.class)
+      .interceptor(EXCEPTION_HANDLER_INTERCEPTOR).build();
 
   private final DatastoreServiceOptions options;
   private final Datastore datastore;
+  private final RetryParams retryParams;
 
   DatastoreServiceImpl(DatastoreServiceOptions options, Datastore datastore) {
     this.options = options;
     this.datastore = datastore;
+    retryParams = MoreObjects.firstNonNull(options.retryParams(), RetryParams.noRetries());
   }
 
   @Override
@@ -49,10 +78,14 @@  QueryResult run(DatastoreV1.ReadOptions readOptionsPb, Query query) {
     return new QueryResultImpl<>(this, readOptionsPb, query);
   }
 
-  DatastoreV1.RunQueryResponse run(DatastoreV1.RunQueryRequest requestPb) {
+  DatastoreV1.RunQueryResponse runQuery(final DatastoreV1.RunQueryRequest requestPb) {
     try {
-      return datastore.runQuery(requestPb);
-    } catch (DatastoreException e) {
+      return RetryHelper.runWithRetries(new Callable() {
+        @Override public DatastoreV1.RunQueryResponse call() throws DatastoreException {
+          return datastore.runQuery(requestPb);
+        }
+      }, retryParams, EXCEPTION_HANDLER);
+    } catch (RetryHelperException e) {
       throw DatastoreServiceException.translateAndThrow(e);
     }
   }
@@ -70,20 +103,26 @@ public Iterator allocateId(PartialKey key, PartialKey... others) {
       requestPb.addKey(trimNameOrId(other).toPb());
     }
     // TODO(ozarov): will need to populate "force" after b/18594027 is fixed.
-    try {
-      DatastoreV1.AllocateIdsResponse responsePb = datastore.allocateIds(requestPb.build());
-      final Iterator keys = responsePb.getKeyList().iterator();
-      return new AbstractIterator() {
+    DatastoreV1.AllocateIdsResponse responsePb = allocateIds(requestPb.build());
+    final Iterator keys = responsePb.getKeyList().iterator();
+    return new AbstractIterator() {
+      @Override protected Key computeNext() {
+        if (keys.hasNext()) {
+          return Key.fromPb(keys.next());
+        }
+        return endOfData();
+      }
+    };
+  }
 
-        @Override
-        protected Key computeNext() {
-          if (keys.hasNext()) {
-            return Key.fromPb(keys.next());
-          }
-          return endOfData();
+  DatastoreV1.AllocateIdsResponse allocateIds(final DatastoreV1.AllocateIdsRequest requestPb) {
+    try {
+      return RetryHelper.runWithRetries(new Callable() {
+        @Override public DatastoreV1.AllocateIdsResponse call() throws DatastoreException {
+          return datastore.allocateIds(requestPb);
         }
-      };
-    } catch (DatastoreException e) {
+      }, retryParams, EXCEPTION_HANDLER);
+    } catch (RetryHelperException e) {
       throw DatastoreServiceException.translateAndThrow(e);
     }
   }
@@ -131,15 +170,11 @@ final class ResultsIterator extends AbstractIterator {
     }
 
     private void loadResults() {
-      try {
-        DatastoreV1.LookupResponse responsePb = datastore.lookup(requestPb.build());
-        iter = responsePb.getFoundList().iterator();
-        requestPb.clearKey();
-        if (responsePb.getDeferredCount() > 0) {
-          requestPb.addAllKey(responsePb.getDeferredList());
-        }
-      } catch (DatastoreException e) {
-        throw DatastoreServiceException.translateAndThrow(e);
+      DatastoreV1.LookupResponse responsePb = lookup(requestPb.build());
+      iter = responsePb.getFoundList().iterator();
+      requestPb.clearKey();
+      if (responsePb.getDeferredCount() > 0) {
+        requestPb.addAllKey(responsePb.getDeferredList());
       }
     }
 
@@ -158,6 +193,18 @@ protected Entity computeNext() {
     }
   }
 
+  DatastoreV1.LookupResponse lookup(final DatastoreV1.LookupRequest requestPb) {
+    try {
+      return RetryHelper.runWithRetries(new Callable() {
+        @Override public DatastoreV1.LookupResponse call() throws DatastoreException {
+          return datastore.lookup(requestPb);
+        }
+      }, retryParams, EXCEPTION_HANDLER);
+    } catch (RetryHelperException e) {
+      throw DatastoreServiceException.translateAndThrow(e);
+    }
+  }
+
   @Override
   public void add(Entity... entities) {
     DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
@@ -215,23 +262,34 @@ private void commitMutation(DatastoreV1.Mutation.Builder mutationPb) {
     DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder();
     requestPb.setMode(DatastoreV1.CommitRequest.Mode.NON_TRANSACTIONAL);
     requestPb.setMutation(mutationPb);
-    commitMutation(requestPb);
+    commit(requestPb.build());
   }
 
-  void commitMutation(DatastoreV1.CommitRequest.Builder requestPb) {
+  DatastoreV1.CommitResponse commit(final DatastoreV1.CommitRequest requestPb) {
     try {
-      datastore.commit(requestPb.build());
-    } catch (DatastoreException e) {
+      return RetryHelper.runWithRetries(new Callable() {
+        @Override public DatastoreV1.CommitResponse call() throws DatastoreException {
+          return datastore.commit(requestPb);
+        }
+      }, retryParams, EXCEPTION_HANDLER);
+    } catch (RetryHelperException e) {
       throw DatastoreServiceException.translateAndThrow(e);
     }
   }
 
   ByteString requestTransactionId(DatastoreV1.BeginTransactionRequest.Builder requestPb) {
+    return beginTransaction(requestPb.build()).getTransaction();
+  }
+
+  DatastoreV1.BeginTransactionResponse beginTransaction(
+      final DatastoreV1.BeginTransactionRequest requestPb) {
     try {
-      DatastoreV1.BeginTransactionResponse responsePb =
-          datastore.beginTransaction(requestPb.build());
-      return responsePb.getTransaction();
-    } catch (DatastoreException e) {
+      return RetryHelper.runWithRetries(new Callable() {
+        @Override public DatastoreV1.BeginTransactionResponse call() throws DatastoreException {
+          return datastore.beginTransaction(requestPb);
+        }
+      }, retryParams, EXCEPTION_HANDLER);
+    } catch (RetryHelperException e) {
       throw DatastoreServiceException.translateAndThrow(e);
     }
   }
@@ -239,9 +297,17 @@ ByteString requestTransactionId(DatastoreV1.BeginTransactionRequest.Builder requ
   void rollbackTransaction(ByteString transaction) {
     DatastoreV1.RollbackRequest.Builder requestPb = DatastoreV1.RollbackRequest.newBuilder();
     requestPb.setTransaction(transaction);
+    rollback(requestPb.build());
+  }
+
+  DatastoreV1.RollbackResponse rollback(final DatastoreV1.RollbackRequest requestPb) {
     try {
-      datastore.rollback(requestPb.build());
-    } catch (DatastoreException e) {
+      return RetryHelper.runWithRetries(new Callable() {
+        @Override public DatastoreV1.RollbackResponse call() throws DatastoreException {
+          return datastore.rollback(requestPb);
+        }
+      }, retryParams, EXCEPTION_HANDLER);
+    } catch (RetryHelperException e) {
       throw DatastoreServiceException.translateAndThrow(e);
     }
   }
diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
index 5639eec89195..5c32f028b6b3 100644
--- a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
@@ -1,6 +1,7 @@
 package com.google.gcloud.datastore;
 
 import com.google.api.services.datastore.DatastoreV1;
+import com.google.api.services.datastore.DatastoreV1.QueryResultBatch.MoreResultsType;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.AbstractIterator;
 import com.google.gcloud.datastore.Query.Type;
@@ -15,7 +16,8 @@ class QueryResultImpl extends AbstractIterator implements QueryResult {
   private final Query.Type queryType;
   private Query query;
   private Query.Type actualType;
-  private DatastoreV1.QueryResultBatch resultPb;
+  private DatastoreV1.QueryResultBatch queryResultBatchPb;
+  private boolean lastBatch;
   private Iterator entityResultPbIter;
   //private ByteString cursor; // only available in v1beta3
 
@@ -37,32 +39,31 @@ class QueryResultImpl extends AbstractIterator implements QueryResult {
     sendRequest();
   }
 
-  private DatastoreV1.QueryResultBatch sendRequest() {
+  private void sendRequest() {
     DatastoreV1.RunQueryRequest.Builder requestPb = DatastoreV1.RunQueryRequest.newBuilder();
     if (readOptionsPb != null) {
       requestPb.setReadOptions(readOptionsPb);
     }
     requestPb.setPartitionId(partitionIdPb);
     query.populatePb(requestPb);
-    resultPb = datastore.run(requestPb.build()).getBatch();
-    entityResultPbIter = resultPb.getEntityResultList().iterator();
+    queryResultBatchPb = datastore.runQuery(requestPb.build()).getBatch();
+    lastBatch = queryResultBatchPb.getMoreResults() != MoreResultsType.NOT_FINISHED;
+    entityResultPbIter = queryResultBatchPb.getEntityResultList().iterator();
     // cursor = resultPb.getSkippedCursor(); // only available in v1beta3
-    actualType = Type.fromPb(resultPb.getEntityResultType());
+    actualType = Type.fromPb(queryResultBatchPb.getEntityResultType());
     if (queryType == Type.PROJECTION) {
       // projection entity can represent all type of results
       actualType = Type.PROJECTION;
     }
     Preconditions.checkState(queryType.isAssignableFrom(actualType),
         "Unexpected result type " + actualType + " vs " + queryType);
-    return resultPb;
   }
 
   @SuppressWarnings("unchecked")
   @Override
   protected T computeNext() {
-    while (!entityResultPbIter.hasNext()
-        && resultPb.getMoreResults() == DatastoreV1.QueryResultBatch.MoreResultsType.NOT_FINISHED) {
-      query = query.nextQuery(resultPb);
+    while (!entityResultPbIter.hasNext() && !lastBatch) {
+      query = query.nextQuery(queryResultBatchPb);
       sendRequest();
     }
     if (!entityResultPbIter.hasNext()) {
diff --git a/src/test/java/com/google/gcloud/RetryHelperTest.java b/src/test/java/com/google/gcloud/RetryHelperTest.java
new file mode 100644
index 000000000000..565d5680df96
--- /dev/null
+++ b/src/test/java/com/google/gcloud/RetryHelperTest.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2012 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;
+
+import static java.util.concurrent.Executors.callable;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Ticker;
+import com.google.gcloud.RetryHelper.NonRetriableException;
+import com.google.gcloud.RetryHelper.RetriesExhaustedException;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Tests for {@link RetryHelper}.
+ */
+public class RetryHelperTest {
+
+  @Test
+  public void testTriesWithExceptionHandling() {
+    assertNull(RetryHelper.getContext());
+    RetryParams params =
+        RetryParams.builder().initialRetryDelayMillis(0).retryMaxAttempts(3).build();
+    ExceptionHandler handler = ExceptionHandler.builder()
+        .retryOn(IOException.class).abortOn(RuntimeException.class).build();
+    final AtomicInteger count = new AtomicInteger(3);
+    try {
+      RetryHelper.runWithRetries(new Callable() {
+        @Override public Void call() throws IOException, NullPointerException {
+          if (count.decrementAndGet() == 2) {
+            assertEquals(1, RetryHelper.getContext().getAttemptNumber());
+            throw new IOException("should be retried");
+          }
+          assertEquals(2, RetryHelper.getContext().getAttemptNumber());
+          throw new NullPointerException("Boo!");
+        }
+      }, params, handler);
+      fail("Exception should have been thrown");
+    } catch (NonRetriableException ex) {
+      assertEquals("Boo!", ex.getCause().getMessage());
+      assertEquals(1, count.intValue());
+    }
+    assertNull(RetryHelper.getContext());
+
+    @SuppressWarnings("serial") class E1 extends Exception {}
+    @SuppressWarnings("serial") class E2 extends E1 {}
+    @SuppressWarnings("serial") class E3 extends E1 {}
+    @SuppressWarnings("serial") class E4 extends E2 {}
+
+    params = RetryParams.builder().initialRetryDelayMillis(0).retryMaxAttempts(5).build();
+    handler = ExceptionHandler.builder().retryOn(E1.class, E4.class).abortOn(E3.class).build();
+    final Iterator exceptions =
+        Arrays.asList(new E1(), new E2(), new E4(), new E3()).iterator();
+    try {
+      RetryHelper.runWithRetries(new Callable() {
+        @Override public Void call() throws E1 {
+          E1 exception = exceptions.next();
+          throw exception;
+        }
+      }, params, handler);
+      fail("Exception should have been thrown");
+    } catch (NonRetriableException ex) {
+      assertTrue(ex.getCause() instanceof E3);
+    }
+    assertNull(RetryHelper.getContext());
+  }
+
+  @Test
+  public void testTriesAtLeastMinTimes() {
+    // Total retry period set to 60 seconds so as to not factor into test
+    RetryParams params = RetryParams.builder().initialRetryDelayMillis(0)
+        .totalRetryPeriodMillis(60000)
+        .retryMinAttempts(5)
+        .retryMaxAttempts(10)
+        .build();
+    final int timesToFail = 7;
+    assertNull(RetryHelper.getContext());
+    int attempted = RetryHelper.runWithRetries(new Callable() {
+      int timesCalled = 0;
+      @Override public Integer call() throws IOException {
+        timesCalled++;
+        assertEquals(timesCalled, RetryHelper.getContext().getAttemptNumber());
+        assertEquals(10, RetryHelper.getContext().getRetryParams().getRetryMaxAttempts());
+        if (timesCalled <= timesToFail) {
+          throw new IOException();
+        }
+        return timesCalled;
+      }
+    }, params, ExceptionHandler.getDefaultInstance());
+    assertEquals(timesToFail + 1, attempted);
+    assertNull(RetryHelper.getContext());
+  }
+
+  @Test
+  public void testTriesNoMoreThanMaxTimes() {
+    // Total retry period set to 60 seconds so as to not factor into test
+    final int maxAttempts = 10;
+    RetryParams params = RetryParams.builder().initialRetryDelayMillis(0)
+        .totalRetryPeriodMillis(60000)
+        .retryMinAttempts(0)
+        .retryMaxAttempts(maxAttempts)
+        .build();
+    final AtomicInteger timesCalled = new AtomicInteger(0);
+    try {
+      RetryHelper.runWithRetries(callable(new Runnable() {
+        @Override public void run() {
+          // Throw an exception up to maxAttempts times, should never be called beyond that
+          if (timesCalled.incrementAndGet() <= maxAttempts) {
+            throw new RuntimeException();
+          }
+          fail("Body was executed too many times: " + timesCalled.get());
+        }
+      }), params, ExceptionHandler.builder().retryOn(RuntimeException.class).build());
+      // Unnecessary as this line should not be possible reach even if RetryHandler is broken
+      fail("Should not have succeeded, expected all attempts to fail and give up.");
+    } catch (RetriesExhaustedException expected) {
+      // Expect the body to run exactly maxAttempts times
+      assertEquals(maxAttempts, timesCalled.get());
+    }
+  }
+
+  private class FakeTicker extends Ticker {
+    private final AtomicLong nanos = new AtomicLong();
+
+    // Advances the ticker value by {@code time} in {@code timeUnit}.
+    FakeTicker advance(long time, TimeUnit timeUnit) {
+      return advance(timeUnit.toNanos(time));
+    }
+
+    // Advances the ticker value by {@code nanoseconds}.
+    FakeTicker advance(long nanoseconds) {
+      nanos.addAndGet(nanoseconds);
+      return this;
+    }
+
+    @Override
+    public long read() {
+      return nanos.get();
+    }
+  }
+
+  @Test
+  public void testTriesNoMoreLongerThanTotalRetryPeriod() {
+    final FakeTicker ticker = new FakeTicker();
+    Stopwatch stopwatch = Stopwatch.createUnstarted(ticker);
+    // The 8th attempt (after min and before max) will trigger a 1 second (virtual) delay exceeding
+    // total retry period which is set just under 1 second. Test occurs faster than realtime.
+    RetryParams params = RetryParams.builder().initialRetryDelayMillis(0)
+        .totalRetryPeriodMillis(999)
+        .retryMinAttempts(5)
+        .retryMaxAttempts(10)
+        .build();
+    ExceptionHandler handler = ExceptionHandler.builder().retryOn(RuntimeException.class).build();
+    final int sleepOnAttempt = 8;
+    final AtomicInteger timesCalled = new AtomicInteger(0);
+    try {
+      RetryHelper.runWithRetries(callable(new Runnable() {
+        @Override public void run() {
+          timesCalled.incrementAndGet();
+          if (timesCalled.get() == sleepOnAttempt) {
+            ticker.advance(1000, TimeUnit.MILLISECONDS);
+          }
+          throw new RuntimeException();
+        }
+      }), params, handler, stopwatch);
+      fail();
+    } catch (RetriesExhaustedException e) {
+      assertEquals(sleepOnAttempt, timesCalled.get());
+    }
+  }
+
+  @Test
+  public void testBackoffIsExponential() {
+    // Total retry period set to 60 seconds so as to not factor into test
+    RetryParams params = RetryParams.builder()
+        .initialRetryDelayMillis(10)
+        .maxRetryDelayMillis(10_000_000)
+        .retryDelayBackoffFactor(2)
+        .totalRetryPeriodMillis(60_000)
+        .retryMinAttempts(0)
+        .retryMaxAttempts(100)
+        .build();
+    long sleepDuration = RetryHelper.getSleepDuration(params, 1);
+    assertTrue("" + sleepDuration, sleepDuration < 13 && sleepDuration >= 7);
+    sleepDuration = RetryHelper.getSleepDuration(params, 2);
+    assertTrue("" + sleepDuration, sleepDuration < 25 && sleepDuration >= 15);
+    sleepDuration = RetryHelper.getSleepDuration(params, 3);
+    assertTrue("" + sleepDuration, sleepDuration < 50 && sleepDuration >= 30);
+    sleepDuration = RetryHelper.getSleepDuration(params, 4);
+    assertTrue("" + sleepDuration, sleepDuration < 100 && sleepDuration >= 60);
+    sleepDuration = RetryHelper.getSleepDuration(params, 5);
+    assertTrue("" + sleepDuration, sleepDuration < 200 && sleepDuration >= 120);
+    sleepDuration = RetryHelper.getSleepDuration(params, 6);
+    assertTrue("" + sleepDuration, sleepDuration < 400 && sleepDuration >= 240);
+    sleepDuration = RetryHelper.getSleepDuration(params, 7);
+    assertTrue("" + sleepDuration, sleepDuration < 800 && sleepDuration >= 480);
+    sleepDuration = RetryHelper.getSleepDuration(params, 8);
+    assertTrue("" + sleepDuration, sleepDuration < 1600 && sleepDuration >= 960);
+    sleepDuration = RetryHelper.getSleepDuration(params, 9);
+    assertTrue("" + sleepDuration, sleepDuration < 3200 && sleepDuration >= 1920);
+    sleepDuration = RetryHelper.getSleepDuration(params, 10);
+    assertTrue("" + sleepDuration, sleepDuration < 6400 && sleepDuration >= 3840);
+    sleepDuration = RetryHelper.getSleepDuration(params, 11);
+    assertTrue("" + sleepDuration, sleepDuration < 12800 && sleepDuration >= 7680);
+    sleepDuration = RetryHelper.getSleepDuration(params, 12);
+    assertTrue("" + sleepDuration, sleepDuration < 25600 && sleepDuration >= 15360);
+  }
+
+  @Test
+  public void testNestedUsage() {
+    assertEquals((1 + 3) * 2, invokeNested(3, 2));
+  }
+
+  private int invokeNested(final int level, final int retries) {
+    if (level < 0) {
+      return 0;
+    }
+    return RetryHelper.runWithRetries(new Callable() {
+      @Override
+      public Integer call() throws IOException {
+        if (RetryHelper.getContext().getAttemptNumber() < retries) {
+          throw new IOException();
+        }
+        assertEquals(retries, RetryHelper.getContext().getAttemptNumber());
+        return invokeNested(level - 1, retries) + RetryHelper.getContext().getAttemptNumber();
+      }
+    });
+  }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
index f0df27a88230..481ee260d500 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
@@ -327,7 +327,7 @@ public void testRunGqlQueryNoCasting() throws DatastoreException {
     requestPb.setGqlQuery(projectionQuery.toPb());
     requestPb.setPartitionId(DatastoreV1.PartitionId.newBuilder().setDatasetId(DATASET).build());
     DatastoreV1.RunQueryResponse responsePb =
-        ((DatastoreServiceImpl) datastore).run(requestPb.build());
+        ((DatastoreServiceImpl) datastore).runQuery(requestPb.build());
     DatastoreV1.RunQueryResponse.Builder responsePbBuilder = responsePb.toBuilder();
     responsePbBuilder.getBatchBuilder()
         .setEntityResultType(DatastoreV1.EntityResult.ResultType.PROJECTION).build();
@@ -406,7 +406,7 @@ public void testRunStructuredQuery() throws DatastoreException {
     requestPb.setQuery(projectionQuery.toPb());
     requestPb.setPartitionId(DatastoreV1.PartitionId.newBuilder().setDatasetId(DATASET).build());
     DatastoreV1.RunQueryResponse responsePb =
-        ((DatastoreServiceImpl) datastore).run(requestPb.build());
+        ((DatastoreServiceImpl) datastore).runQuery(requestPb.build());
     DatastoreV1.RunQueryResponse.Builder responsePbBuilder = responsePb.toBuilder();
     responsePbBuilder.getBatchBuilder()
         .setEntityResultType(DatastoreV1.EntityResult.ResultType.PROJECTION).build();

From 509072d2347d09fc556b91a089006ef4b94f199d Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Sun, 28 Dec 2014 16:57:04 -0800
Subject: [PATCH 071/771] work in progress

---
 .classpath                                    |  1 +
 .project                                      | 13 +++++++
 .../com/google/gcloud/ServiceOptions.java     |  8 +++-
 .../java/com/google/gcloud/storage/Acl.java   | 39 +++++++++++++++++++
 .../com/google/gcloud/storage/Bucket.java     |  6 +--
 .../java/com/google/gcloud/storage/Key.java   | 26 +++++++++++++
 .../google/gcloud/storage/StorageObject.java  | 15 +++++++
 .../google/gcloud/storage/StorageService.java |  1 -
 .../gcloud/storage/StorageServiceImpl.java    |  2 +-
 9 files changed, 105 insertions(+), 6 deletions(-)
 create mode 100644 src/main/java/com/google/gcloud/storage/Key.java
 create mode 100644 src/main/java/com/google/gcloud/storage/StorageObject.java

diff --git a/.classpath b/.classpath
index d6cf6121af66..dc5384546f80 100644
--- a/.classpath
+++ b/.classpath
@@ -20,6 +20,7 @@
 	
 		
 			
+			
 		
 	
 	
diff --git a/.project b/.project
index fac3e5997ca3..d31c8122faa2 100644
--- a/.project
+++ b/.project
@@ -5,6 +5,11 @@
 	
 	
 	
+		
+			org.eclipse.wst.common.project.facet.core.builder
+			
+			
+		
 		
 			org.eclipse.jdt.core.javabuilder
 			
@@ -30,11 +35,19 @@
 			
 			
 		
+		
+			org.eclipse.wst.validation.validationbuilder
+			
+			
+		
 	
 	
+		org.eclipse.jem.workbench.JavaEMFNature
+		org.eclipse.wst.common.modulecore.ModuleCoreNature
 		org.eclipse.jdt.core.javanature
 		org.eclipse.m2e.core.maven2Nature
 		net.sf.eclipsecs.core.CheckstyleNature
 		edu.umd.cs.findbugs.plugin.eclipse.findbugsNature
+		org.eclipse.wst.common.project.facet.core.nature
 	
 
diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java
index 0153d364e365..a86897349bc0 100644
--- a/src/main/java/com/google/gcloud/ServiceOptions.java
+++ b/src/main/java/com/google/gcloud/ServiceOptions.java
@@ -63,7 +63,13 @@ private static AuthConfig defaultAuthConfig() {
   }
 
   protected static String appEngineAppId() {
-    return System.getProperty("com.google.appengine.application.id");
+    try {
+      Class apiProxy = Class.forName("com.google.apphosting.api.ApiProxy");
+      Object currentEnv = apiProxy.getMethod("getCurrentEnvironment").invoke(null);
+      return (String) currentEnv.getClass().getMethod("getAppId").invoke(currentEnv);
+    } catch (Exception ex) {
+      return System.getProperty("com.google.appengine.application.id");
+    }
   }
 
   protected abstract static class Builder> {
diff --git a/src/main/java/com/google/gcloud/storage/Acl.java b/src/main/java/com/google/gcloud/storage/Acl.java
index 06f2c467bbaf..37cd880958f7 100644
--- a/src/main/java/com/google/gcloud/storage/Acl.java
+++ b/src/main/java/com/google/gcloud/storage/Acl.java
@@ -2,4 +2,43 @@
 
 public interface Acl {
 
+  public class ProjectTeam {
+    	// ProjectNumber: The project number.
+	//ProjectNumber string `json:"projectNumber,omitempty"`
+
+	// Team: The team. Can be owners, editors, or viewers.
+	//Team string `json:"team,omitempty"`
+  }
+
+  enum Entity {
+    USER_ID("user-userId"),
+    USER_EMAIL("user-emailAddress"),
+    GROUP_ID("group-groupId"),
+    GROUP_EMAIL("group-emailAddress"),
+    ALL_USERS("allUsers"),
+    ALL_AUTHENTICATED_USERS("allAuthenticatedUsers");
+
+    private final String value;
+
+    Entity(String value) {
+      this.value = value;
+    }
+  }
+
+  String domain();
+
+  Entity entity();
+
+  String entityId();
+
+  String email();
+
+  String etag();
+
+  String generation();
+
+
+  ProjectTeam projectTeam();
+
+  String role();
 }
diff --git a/src/main/java/com/google/gcloud/storage/Bucket.java b/src/main/java/com/google/gcloud/storage/Bucket.java
index f0f9f8f4b52f..2149cafca6f6 100644
--- a/src/main/java/com/google/gcloud/storage/Bucket.java
+++ b/src/main/java/com/google/gcloud/storage/Bucket.java
@@ -14,11 +14,11 @@ public interface Bucket {
 
   void updateDefaultObjectAcl();
 
-  Acl acl(String objectName);
 
-  void updateAcl(String objectName, Acl acl);
 
-  void delete(String... objectName);
+
+
+  void delete(Key... objectKey);
 
   void compose(Iterable source, String dest);
 
diff --git a/src/main/java/com/google/gcloud/storage/Key.java b/src/main/java/com/google/gcloud/storage/Key.java
new file mode 100644
index 000000000000..759cc5e9496f
--- /dev/null
+++ b/src/main/java/com/google/gcloud/storage/Key.java
@@ -0,0 +1,26 @@
+package com.google.gcloud.storage;
+
+public class Key {
+
+  // TODO: add builder, factory method, toURL, from URL, equals,hashCode, toString
+  private final String bucket;
+  private final String name;
+
+  /*
+  Builder() {
+
+  }*/
+
+  Key(String bucket, String name) {
+    this.bucket = bucket;
+    this.name = name;
+  }
+
+  public String bucket() {
+    return bucket;
+  }
+
+  public String name() {
+    return name;
+  }
+}
diff --git a/src/main/java/com/google/gcloud/storage/StorageObject.java b/src/main/java/com/google/gcloud/storage/StorageObject.java
new file mode 100644
index 000000000000..0fbee66031b8
--- /dev/null
+++ b/src/main/java/com/google/gcloud/storage/StorageObject.java
@@ -0,0 +1,15 @@
+package com.google.gcloud.storage;
+
+import java.nio.ByteBuffer;
+
+public interface StorageObject {
+
+  // builder will have an option to populate content and set acl, bucket, name,..
+
+  Key key();
+
+  Acl acl();
+
+  ByteBuffer content();
+
+}
diff --git a/src/main/java/com/google/gcloud/storage/StorageService.java b/src/main/java/com/google/gcloud/storage/StorageService.java
index 52e2c879c0f8..47e2af985f3a 100644
--- a/src/main/java/com/google/gcloud/storage/StorageService.java
+++ b/src/main/java/com/google/gcloud/storage/StorageService.java
@@ -5,5 +5,4 @@ public interface StorageService {
   Iterable listBuckets();
 
   Bucket getBucket(String bucket);
-
 }
diff --git a/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java b/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java
index 135bff666c0f..85c57bea3a9b 100644
--- a/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java
+++ b/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java
@@ -9,7 +9,7 @@ final class StorageServiceImpl implements StorageService {
 
   StorageServiceImpl(StorageServiceOptions options) {
     this.options = options;
-    this.storage = options.getStorage();
+    storage = options.getStorage();
   }
 
   @Override

From 7a23a3e4c6e0aefe292b375f0c0cf99f41a6d648 Mon Sep 17 00:00:00 2001
From: aozarov 
Date: Mon, 29 Dec 2014 17:51:41 -0800
Subject: [PATCH 072/771] fix datastore exception handling and auth handling

---
 pom.xml                                       | 23 +++++++----
 .../java/com/google/gcloud/AuthConfig.java    | 29 ++++++++------
 .../com/google/gcloud/ServiceOptions.java     |  2 +-
 .../datastore/DatastoreServiceException.java  | 39 +++++++++++++------
 .../google/gcloud/datastore/PartialKey.java   |  2 +-
 .../google/gcloud/datastore/PathElement.java  |  2 +-
 .../datastore/DatastoreServiceTest.java       |  3 ++
 7 files changed, 68 insertions(+), 32 deletions(-)

diff --git a/pom.xml b/pom.xml
index 1f8cb504a65c..cccce5ebbadf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,8 +21,7 @@
     
       com.google.guava
       guava
-      RELEASE
-      compile
+      18.0
     
     
       com.google.apis
@@ -35,11 +34,17 @@
       google-api-client-appengine
       1.19.0
       compile
+      
+        
+          guava-jdk5
+          com.google.guava
+        
+      
     
     
       junit
       junit
-      RELEASE
+      4.12
       test
     
     
@@ -66,10 +71,10 @@
       v1beta2-rev23-1.19.0
     
     
-    	org.easymock
-    	easymock
-    	3.3
-    	test
+      org.easymock
+      easymock
+      3.3
+      test
     
   
   
@@ -97,6 +102,10 @@
   
   
     
+      
+        maven-jar-plugin
+        2.5
+      
       
         maven-compiler-plugin
         3.1
diff --git a/src/main/java/com/google/gcloud/AuthConfig.java b/src/main/java/com/google/gcloud/AuthConfig.java
index ddae5ffa1c72..67e7b572c133 100644
--- a/src/main/java/com/google/gcloud/AuthConfig.java
+++ b/src/main/java/com/google/gcloud/AuthConfig.java
@@ -1,5 +1,7 @@
 package com.google.gcloud;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
 import com.google.api.client.googleapis.compute.ComputeCredential;
 import com.google.api.client.googleapis.extensions.appengine.auth.oauth2.AppIdentityCredential;
@@ -8,7 +10,6 @@
 import com.google.api.client.http.HttpTransport;
 import com.google.api.client.http.javanet.NetHttpTransport;
 import com.google.api.client.json.jackson.JacksonFactory;
-import com.google.common.base.Preconditions;
 
 import java.io.IOException;
 import java.security.GeneralSecurityException;
@@ -31,12 +32,14 @@ private static class ServiceAccountAuthConfig extends AuthConfig {
     private final String account;
     private final PrivateKey privateKey;
 
-    public ServiceAccountAuthConfig(String account, PrivateKey privateKey) {
-      this.account = account;
-      this.privateKey = privateKey;
-      if (privateKey != null) {
-        Preconditions.checkArgument(account != null);
-      }
+    ServiceAccountAuthConfig(String account, PrivateKey privateKey) {
+      this.account = checkNotNull(account);
+      this.privateKey = checkNotNull(privateKey);
+    }
+
+    ServiceAccountAuthConfig() {
+      account = null;
+      privateKey = null;
     }
 
     @Override
@@ -44,10 +47,10 @@ protected HttpRequestInitializer httpRequestInitializer(
         HttpTransport transport, Set scopes) {
       GoogleCredential.Builder builder = new GoogleCredential.Builder()
           .setTransport(transport)
-          .setJsonFactory(new JacksonFactory())
-          .setServiceAccountId(account)
-          .setServiceAccountPrivateKey(privateKey);
+          .setJsonFactory(new JacksonFactory());
       if (privateKey != null) {
+        builder.setServiceAccountPrivateKey(privateKey);
+        builder.setServiceAccountId(account);
         builder.setServiceAccountScopes(scopes);
       }
       return builder.build();
@@ -72,10 +75,14 @@ protected HttpRequestInitializer httpRequestInitializer(HttpTransport ts, Set REASON_TO_CODE;
+  private static final ImmutableMap HTTP_TO_CODE;
 
   private final Code code;
 
@@ -24,22 +28,29 @@ public class DatastoreServiceException extends RuntimeException {
    */
   public enum Code {
 
-    ABORTED(true, "Request aborted"),
-    DEADLINE_EXCEEDED(true, "Deadline exceeded"),
-    UNAVAILABLE(true, "Could not reach service"),
-    FAILED_PRECONDITION(false, "Invalid request"),
-    INVALID_ARGUMENT(false, "Request parameter has an invalid value"),
-    PERMISSION_DENIED(false, "Unauthorized request"),
-    RESOURCE_EXHAUSTED(false, "Quota exceeded"),
-    INTERNAL(false, "Server returned an error"),
-    UNKNOWN(false, "Unknown failure");
+    ABORTED(true, "Request aborted", 409),
+    DEADLINE_EXCEEDED(true, "Deadline exceeded", 403),
+    UNAVAILABLE(true, "Could not reach service", 503),
+    FAILED_PRECONDITION(false, "Invalid request", 412),
+    INVALID_ARGUMENT(false, "Request parameter has an invalid value", 400),
+    PERMISSION_DENIED(false, "Unauthorized request", 403),
+    UNAUTHORIZED(false, "Unauthorized", 401),
+    RESOURCE_EXHAUSTED(false, "Quota exceeded", 402),
+    INTERNAL(false, "Server returned an error", 500),
+    UNKNOWN(false, "Unknown failure", -1);
 
     private final boolean isTransient;
     private final String defaultMessage;
+    private final int httpCode;
 
-    Code(boolean isTransient, String msg) {
+    Code(boolean isTransient, String msg, int httpCode) {
       this.isTransient = isTransient;
       defaultMessage = msg;
+      this.httpCode = httpCode;
+    }
+
+    public Integer httpCode() {
+      return httpCode;
     }
 
     /**
@@ -57,10 +68,13 @@ DatastoreServiceException translate(DatastoreException exception, String msg) {
 
   static {
     ImmutableMap.Builder builder = ImmutableMap.builder();
+    Map httpCodes = new HashMap<>();
     for (Code code : Code.values()) {
       builder.put(code.name(), code);
+      httpCodes.put(code.httpCode(), code);
     }
     REASON_TO_CODE = builder.build();
+    HTTP_TO_CODE = ImmutableMap.copyOf(httpCodes);
   }
 
   public DatastoreServiceException(Code code, String msg, Exception cause) {
@@ -108,7 +122,10 @@ static DatastoreServiceException translateAndThrow(DatastoreException exception)
         // ignore - will be converted to unknown
       }
     }
-    Code code = MoreObjects.firstNonNull(REASON_TO_CODE.get(reason), Code.UNKNOWN);
+    Code code = REASON_TO_CODE.get(reason);
+    if (code == null) {
+      code = MoreObjects.firstNonNull(HTTP_TO_CODE.get(exception.getCode()), Code.UNKNOWN);
+    }
     throw code.translate(exception, message);
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java
index 302abbd065c8..e819aab8ec0b 100644
--- a/src/main/java/com/google/gcloud/datastore/PartialKey.java
+++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java
@@ -1,7 +1,7 @@
 package com.google.gcloud.datastore;
 
-import com.google.api.client.repackaged.com.google.common.base.Preconditions;
 import com.google.api.services.datastore.DatastoreV1;
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.protobuf.InvalidProtocolBufferException;
 
diff --git a/src/main/java/com/google/gcloud/datastore/PathElement.java b/src/main/java/com/google/gcloud/datastore/PathElement.java
index 5652359f2818..9e84764e55f7 100644
--- a/src/main/java/com/google/gcloud/datastore/PathElement.java
+++ b/src/main/java/com/google/gcloud/datastore/PathElement.java
@@ -1,6 +1,6 @@
 package com.google.gcloud.datastore;
 
-import static com.google.api.client.repackaged.com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.api.services.datastore.DatastoreV1;
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
index 481ee260d500..a693446afc1e 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
@@ -19,6 +19,8 @@
 import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 import java.util.Collections;
 import java.util.HashMap;
@@ -26,6 +28,7 @@
 import java.util.List;
 import java.util.Map;
 
+@RunWith(JUnit4.class)
 public class DatastoreServiceTest {
 
   private static final String DATASET = "dataset1";

From ede68673eb8d66a7404454e84ba2e1f0b6d5bc97 Mon Sep 17 00:00:00 2001
From: aozarov 
Date: Tue, 30 Dec 2014 19:48:42 -0800
Subject: [PATCH 073/771] Remove the need to provide a full application name
 for dataset (s~ | e~).

---
 .checkstyle                                   |   2 +-
 .eclipse-pmd                                  |   7 +
 .project                                      |  12 +
 checkstyle.xml                                |   8 +-
 full-pmd-ruleset.xml                          |  46 +++
 pmd.xml                                       | 336 ++++++++++++++++++
 .../com/google/gcloud/ExceptionHandler.java   |  59 +--
 .../com/google/gcloud/ServiceOptions.java     | 109 +++---
 .../datastore/DatastoreServiceImpl.java       |  11 +-
 .../datastore/DatastoreServiceOptions.java    |  84 +++--
 .../java/com/google/gcloud/storage/Acl.java   |   8 +-
 .../google/gcloud/ExceptionHandlerTest.java   |  12 +-
 .../com/google/gcloud/RetryHelperTest.java    |  17 +-
 .../datastore/DatastoreServiceTest.java       |   8 +
 14 files changed, 609 insertions(+), 110 deletions(-)
 create mode 100644 .eclipse-pmd
 create mode 100644 full-pmd-ruleset.xml
 create mode 100644 pmd.xml

diff --git a/.checkstyle b/.checkstyle
index 5783bc0d77a1..429677b11adc 100644
--- a/.checkstyle
+++ b/.checkstyle
@@ -1,7 +1,7 @@
 
 
 
-  
+  
     
   
 
diff --git a/.eclipse-pmd b/.eclipse-pmd
new file mode 100644
index 000000000000..c14648afb674
--- /dev/null
+++ b/.eclipse-pmd
@@ -0,0 +1,7 @@
+
+
+  
+  
+    
+  
+
\ No newline at end of file
diff --git a/.project b/.project
index d31c8122faa2..0443050b010d 100644
--- a/.project
+++ b/.project
@@ -40,6 +40,16 @@
 			
 			
 		
+		
+			ntut.csie.rleht.builder.RLBuilder
+			
+			
+		
+		
+			ch.acanda.eclipse.pmd.builder.PMDBuilder
+			
+			
+		
 	
 	
 		org.eclipse.jem.workbench.JavaEMFNature
@@ -49,5 +59,7 @@
 		net.sf.eclipsecs.core.CheckstyleNature
 		edu.umd.cs.findbugs.plugin.eclipse.findbugsNature
 		org.eclipse.wst.common.project.facet.core.nature
+		ntut.csie.rleht.builder.RLNature
+		ch.acanda.eclipse.pmd.builder.PMDNature
 	
 
diff --git a/checkstyle.xml b/checkstyle.xml
index a3c9eecc8e46..814915090b25 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -118,10 +118,12 @@ Checkstyle configurartion that checks the Google coding conventions (https://goo
     
     
     
+      
       
       
       
       
+      
     
     
     
@@ -157,7 +159,11 @@ Checkstyle configurartion that checks the Google coding conventions (https://goo
       
     
     
-    
+    
+      
+      
+      
+    
   
   
     
diff --git a/full-pmd-ruleset.xml b/full-pmd-ruleset.xml
new file mode 100644
index 000000000000..9f1ec73cc736
--- /dev/null
+++ b/full-pmd-ruleset.xml
@@ -0,0 +1,46 @@
+
+
+   Full 5.1.1 PMD rule set
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+
diff --git a/pmd.xml b/pmd.xml
new file mode 100644
index 000000000000..0b206c082bb3
--- /dev/null
+++ b/pmd.xml
@@ -0,0 +1,336 @@
+
+
+   PMD Plugin preferences rule set
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+   
+
\ No newline at end of file
diff --git a/src/main/java/com/google/gcloud/ExceptionHandler.java b/src/main/java/com/google/gcloud/ExceptionHandler.java
index 9ad46f9d1040..3cb6941d428c 100644
--- a/src/main/java/com/google/gcloud/ExceptionHandler.java
+++ b/src/main/java/com/google/gcloud/ExceptionHandler.java
@@ -1,5 +1,6 @@
 package com.google.gcloud;
 
+import static com.google.common.base.MoreObjects.firstNonNull;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -7,6 +8,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
+import com.google.gcloud.ExceptionHandler.Interceptor.RetryResult;
 
 import java.io.Serializable;
 import java.lang.reflect.Method;
@@ -30,25 +32,42 @@ public final class ExceptionHandler implements Serializable {
 
   public interface Interceptor extends Serializable {
 
+    enum RetryResult {
+
+      RETRY(true),
+      ABORT(false);
+
+      private final boolean booleanValue;
+
+      private RetryResult(boolean booleanValue) {
+        this.booleanValue = booleanValue;
+      }
+
+      boolean booleanValue() {
+        return booleanValue;
+      }
+    }
+
     /**
-     * This method is called before evaluating if the exception should be propagated
-     * and could short-circuit the evaluation process.
+     * This method is called before exception evaluation and could short-circuit the process.
      *
      * @param exception the exception that is being evaluated
-     * @return {@code Boolean.TRUE} if exception should be ignored, {@code Boolean.FALSE}
-     *      if exception should be propagated or {@code null} if evaluation should proceed.
+     * @return {@link RetryResult} to indicate if the exception should be ignored
+     *     ({@link RetryResult#RETRY}), propagated ({@link RetryResult#ABORT}),
+     *     or evaluation should proceed ({@code null}).
      */
-    Boolean shouldRetry(Exception exception);
+    RetryResult shouldRetry(Exception exception);
 
     /**
-     * This method is called after the evaluation but could alter the result if desired.
+     * This method is called after the evaluation and could alter its result.
      *
      * @param exception the exception that is being evaluated
-     * @param shouldRetry the result of the evaluation
-     * @return {@code true} if exception should be ignored or {@code false}
-     *      if exception should be propagated.
+     * @param retryResult the result of the evaluation so far.
+     * @return {@link RetryResult} to indicate if the exception should be ignored
+     *     ({@link RetryResult#RETRY}), propagated ({@link RetryResult#ABORT}),
+     *     or evaluation should proceed ({@code null}).
      */
-    boolean shouldRetry(Exception exception, boolean shouldRetry);
+    RetryResult shouldRetry(Exception exception, RetryResult retryResult);
   }
 
   /**
@@ -121,10 +140,10 @@ static final class RetryInfo implements Serializable {
 
     private static final long serialVersionUID = -4264634837841455974L;
     private final Class exception;
-    private final boolean retry;
+    private final RetryResult retry;
     private final Set children = Sets.newHashSet();
 
-    RetryInfo(Class exception, boolean retry) {
+    RetryInfo(Class exception, RetryResult retry) {
       this.exception = checkNotNull(exception);
       this.retry = retry;
     }
@@ -155,10 +174,10 @@ private ExceptionHandler(Builder builder) {
         Sets.intersection(retriableExceptions, nonRetriableExceptions).isEmpty(),
         "Same exception was found in both retriable and non-retriable sets");
     for (Class exception : retriableExceptions) {
-      addToRetryInfos(retryInfos, new RetryInfo(exception, true));
+      addToRetryInfos(retryInfos, new RetryInfo(exception, RetryResult.RETRY));
     }
     for (Class exception : nonRetriableExceptions) {
-      addToRetryInfos(retryInfos,  new RetryInfo(exception, false));
+      addToRetryInfos(retryInfos,  new RetryInfo(exception, RetryResult.ABORT));
     }
   }
 
@@ -223,17 +242,17 @@ public Set> getNonRetriableExceptions() {
 
   boolean shouldRetry(Exception ex) {
     for (Interceptor interceptor : interceptors) {
-      Boolean shouldRetry = interceptor.shouldRetry(ex);
-      if (shouldRetry != null) {
-        return shouldRetry.booleanValue();
+      RetryResult retryResult = interceptor.shouldRetry(ex);
+      if (retryResult != null) {
+        return retryResult.booleanValue();
       }
     }
     RetryInfo retryInfo = findMostSpecificRetryInfo(retryInfos, ex.getClass());
-    boolean shouldRetry = retryInfo == null ? false : retryInfo.retry;
+    RetryResult retryResult = retryInfo == null ? RetryResult.ABORT : retryInfo.retry;
     for (Interceptor interceptor : interceptors) {
-      shouldRetry = interceptor.shouldRetry(ex, shouldRetry);
+      retryResult = firstNonNull(interceptor.shouldRetry(ex, retryResult), retryResult);
     }
-    return shouldRetry;
+    return retryResult.booleanValue();
   }
 
   /**
diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java
index 93c9af4b6136..c23d754a5c46 100644
--- a/src/main/java/com/google/gcloud/ServiceOptions.java
+++ b/src/main/java/com/google/gcloud/ServiceOptions.java
@@ -8,6 +8,11 @@
 import com.google.api.client.http.HttpTransport;
 import com.google.api.client.http.javanet.NetHttpTransport;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
 import java.util.Set;
 
 public abstract class ServiceOptions {
@@ -19,6 +24,50 @@ public abstract class ServiceOptions {
   private final AuthConfig authConfig;
   private final RetryParams retryParams;
 
+  protected abstract static class Builder> {
+
+    private String host;
+    private HttpTransport httpTransport;
+    private AuthConfig authConfig;
+    private RetryParams retryParams;
+
+    protected Builder() {}
+
+    protected Builder(ServiceOptions options) {
+      host = options.host;
+      httpTransport = options.httpTransport;
+      authConfig = options.authConfig;
+      retryParams = options.retryParams;
+    }
+
+    protected abstract ServiceOptions build();
+
+    @SuppressWarnings("unchecked")
+    protected B self() {
+      return (B) this;
+    }
+
+    public B host(String host) {
+      this.host = host;
+      return self();
+    }
+
+    public B httpTransport(HttpTransport httpTransport) {
+      this.httpTransport = httpTransport;
+      return self();
+    }
+
+    public B authConfig(AuthConfig authConfig) {
+      this.authConfig = authConfig;
+      return self();
+    }
+
+    public B retryParams(RetryParams retryParams) {
+      this.retryParams = retryParams;
+      return self();
+    }
+  }
+
   protected ServiceOptions(Builder builder) {
     host = firstNonNull(builder.host, DEFAULT_HOST);
     httpTransport = firstNonNull(builder.httpTransport, defaultHttpTransport());
@@ -63,56 +112,20 @@ private static AuthConfig defaultAuthConfig() {
   }
 
   protected static String appEngineAppId() {
-    try {
-      Class apiProxy = Class.forName("com.google.apphosting.api.ApiProxy");
-      Object currentEnv = apiProxy.getMethod("getCurrentEnvironment").invoke(null);
-      return (String) currentEnv.getClass().getMethod("getAppId").invoke(currentEnv);
-    } catch (Exception ex) {
-      return System.getProperty("com.google.appengine.application.id");
-    }
+    return System.getProperty("com.google.appengine.application.id");
   }
 
-  protected abstract static class Builder> {
-
-    private String host;
-    private HttpTransport httpTransport;
-    private AuthConfig authConfig;
-    private RetryParams retryParams;
-
-    protected Builder() {}
-
-    protected Builder(ServiceOptions options) {
-      host = options.host;
-      httpTransport = options.httpTransport;
-      authConfig = options.authConfig;
-      retryParams = options.retryParams;
-    }
-
-    protected abstract ServiceOptions build();
-
-    @SuppressWarnings("unchecked")
-    protected B self() {
-      return (B) this;
-    }
-
-    public B host(String host) {
-      this.host = host;
-      return self();
-    }
-
-    public B httpTransport(HttpTransport httpTransport) {
-      this.httpTransport = httpTransport;
-      return self();
-    }
-
-    public B authConfig(AuthConfig authConfig) {
-      this.authConfig = authConfig;
-      return self();
-    }
-
-    public B retryParams(RetryParams retryParams) {
-      this.retryParams = retryParams;
-      return self();
+  protected static String googleCloudProjectId() {
+    try {
+      URL url = new URL("http://metadata/computeMetadata/v1/project/project-id");
+      URLConnection connection = url.openConnection();
+      connection.setRequestProperty("X-Google-Metadata-Request", "True");
+      try (BufferedReader reader =
+          new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
+        return reader.readLine();
+      }
+    } catch (IOException e) {
+      return null;
     }
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
index 875660de74b8..a887c03076df 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
@@ -28,14 +28,17 @@ final class DatastoreServiceImpl implements DatastoreService {
         private static final long serialVersionUID = 6911242958397733203L;
 
         @Override
-        public boolean shouldRetry(Exception exception, boolean shouldRetry) {
-          return shouldRetry;
+        public RetryResult shouldRetry(Exception exception, RetryResult retryResult) {
+          return null;
         }
 
         @Override
-        public Boolean shouldRetry(Exception exception) {
+        public RetryResult shouldRetry(Exception exception) {
           if (exception instanceof DatastoreServiceException) {
-            return ((DatastoreServiceException) exception).code().isTransient();
+            boolean isTransient = ((DatastoreServiceException) exception).code().isTransient();
+            return isTransient
+                ? ExceptionHandler.Interceptor.RetryResult.RETRY
+                : ExceptionHandler.Interceptor.RetryResult.ABORT;
           }
           return null;
         }
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java
index 3b81cac646c4..f3cc48d69801 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java
@@ -1,21 +1,28 @@
 package com.google.gcloud.datastore;
 
 import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.gcloud.datastore.Validator.validateDataset;
 import static com.google.gcloud.datastore.Validator.validateNamespace;
 
+import com.google.api.client.http.HttpRequestInitializer;
+import com.google.api.services.datastore.DatastoreV1;
+import com.google.api.services.datastore.DatastoreV1.EntityResult;
+import com.google.api.services.datastore.DatastoreV1.LookupResponse;
 import com.google.api.services.datastore.client.Datastore;
+import com.google.api.services.datastore.client.DatastoreException;
 import com.google.api.services.datastore.client.DatastoreFactory;
 import com.google.api.services.datastore.client.DatastoreOptions;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.gcloud.ServiceOptions;
 
 import java.lang.reflect.Method;
+import java.util.Iterator;
 import java.util.Set;
 
 public class DatastoreServiceOptions extends ServiceOptions {
 
+  private static final String DATASET_ENV_NAME = "DATASTORE_DATASET";
   private static final String DATASTORE_SCOPE = "https://www.googleapis.com/auth/datastore";
   private static final String USERINFO_SCOPE = "https://www.googleapis.com/auth/userinfo.email";
   private static final Set SCOPES = ImmutableSet.of(DATASTORE_SCOPE, USERINFO_SCOPE);
@@ -24,15 +31,6 @@ public class DatastoreServiceOptions extends ServiceOptions {
   private final boolean force;
   private final Datastore datastore;
 
-  DatastoreServiceOptions(Builder builder) {
-    super(builder);
-    dataset = firstNonNull(builder.dataset, appEngineAppId());
-    checkArgument(dataset != null, "missing dataset");
-    namespace = builder.namespace != null ? builder.namespace : defaultNamespace();
-    force = builder.force;
-    datastore = builder.datastore;
-  }
-
   public static class Builder extends ServiceOptions.Builder {
 
     private String dataset;
@@ -54,7 +52,7 @@ public DatastoreServiceOptions build() {
       return new DatastoreServiceOptions(this);
     }
 
-    Builder datastore(Datastore datastore) {
+    public Builder datastore(Datastore datastore) {
       this.datastore = datastore;
       return this;
     }
@@ -75,6 +73,58 @@ public Builder force(boolean force) {
     }
   }
 
+  DatastoreServiceOptions(Builder builder) {
+    super(builder);
+    namespace = builder.namespace != null ? builder.namespace : defaultNamespace();
+    force = builder.force;
+
+    // Replace provided dataset with full dataset (s~xxx, e~xxx,...)
+    String tempDataset = firstNonNull(builder.dataset, defaultDataset());
+    Datastore tempDatastore = firstNonNull(builder.datastore,
+        defaultDatastore(tempDataset, host(), httpRequestInitializer()));
+    DatastoreV1.LookupRequest.Builder requestPb = DatastoreV1.LookupRequest.newBuilder();
+    DatastoreV1.Key key = DatastoreV1.Key.newBuilder()
+        .addPathElement(DatastoreV1.Key.PathElement.newBuilder().setKind("__foo__").setName("bar"))
+        .build();
+    requestPb.addKey(key);
+    try {
+      LookupResponse responsePb = tempDatastore.lookup(requestPb.build());
+      if (responsePb.getDeferredCount() > 0) {
+        key = responsePb.getDeferred(0);
+      } else {
+        Iterator combinedIter =
+            Iterables.concat(responsePb.getMissingList(), responsePb.getFoundList()).iterator();
+        key = combinedIter.next().getEntity().getKey();
+      }
+      dataset = key.getPartitionId().getDatasetId();
+      if (builder.datastore == null && !dataset.equals(tempDataset)) {
+        datastore = defaultDatastore(dataset, host(), httpRequestInitializer());
+      } else {
+        datastore = tempDatastore;
+      }
+    } catch (DatastoreException e) {
+      throw DatastoreServiceException.translateAndThrow(e);
+    }
+  }
+
+  private static Datastore defaultDatastore(
+      String dataset, String host, HttpRequestInitializer initializer) {
+    DatastoreOptions options = new DatastoreOptions.Builder()
+        .dataset(dataset)
+        .host(host)
+        .initializer(initializer)
+        .build();
+    return DatastoreFactory.get().create(options);
+  }
+
+  private static String defaultDataset() {
+    String dataset = System.getProperty(DATASET_ENV_NAME, System.getenv(DATASET_ENV_NAME));
+    if (dataset == null) {
+      dataset = appEngineAppId();
+    }
+    return dataset != null ? dataset : googleCloudProjectId();
+  }
+
   public String dataset() {
     return dataset;
   }
@@ -110,16 +160,8 @@ public Builder toBuilder() {
     return new Builder(this);
   }
 
-  private DatastoreOptions toDatastoreOptions() {
-    return new DatastoreOptions.Builder()
-        .dataset(dataset())
-        .host(host())
-        .initializer(httpRequestInitializer())
-        .build();
-  }
-
-  Datastore datastore() {
-    return datastore != null ? datastore : DatastoreFactory.get().create(toDatastoreOptions());
+  public Datastore datastore() {
+    return datastore;
   }
 
   public static Builder builder() {
diff --git a/src/main/java/com/google/gcloud/storage/Acl.java b/src/main/java/com/google/gcloud/storage/Acl.java
index 37cd880958f7..13bbe2bfb44d 100644
--- a/src/main/java/com/google/gcloud/storage/Acl.java
+++ b/src/main/java/com/google/gcloud/storage/Acl.java
@@ -3,11 +3,11 @@
 public interface Acl {
 
   public class ProjectTeam {
-    	// ProjectNumber: The project number.
-	//ProjectNumber string `json:"projectNumber,omitempty"`
+      // ProjectNumber: The project number.
+  //ProjectNumber string `json:"projectNumber,omitempty"`
 
-	// Team: The team. Can be owners, editors, or viewers.
-	//Team string `json:"team,omitempty"`
+  // Team: The team. Can be owners, editors, or viewers.
+  //Team string `json:"team,omitempty"`
   }
 
   enum Entity {
diff --git a/src/test/java/com/google/gcloud/ExceptionHandlerTest.java b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java
index 42905c85a08a..35f9613c42ae 100644
--- a/src/test/java/com/google/gcloud/ExceptionHandlerTest.java
+++ b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java
@@ -5,6 +5,7 @@
 import static org.junit.Assert.fail;
 
 import com.google.gcloud.ExceptionHandler.Interceptor;
+import com.google.gcloud.ExceptionHandler.Interceptor.RetryResult;
 
 import org.junit.Test;
 
@@ -112,16 +113,15 @@ public void testShouldTry() {
     assertFalse(handler.shouldRetry(new RuntimeException()));
     assertTrue(handler.shouldRetry(new NullPointerException()));
 
-    final AtomicReference before = new AtomicReference<>(false);
-
+    final AtomicReference before = new AtomicReference<>(RetryResult.ABORT);
     Interceptor interceptor = new Interceptor() {
       @Override
-      public boolean shouldRetry(Exception exception, boolean shouldRetry) {
-        return !shouldRetry;
+      public RetryResult shouldRetry(Exception exception, RetryResult retryResult) {
+        return retryResult == RetryResult.ABORT ? RetryResult.RETRY : RetryResult.ABORT;
       }
 
       @Override
-      public Boolean shouldRetry(Exception exception) {
+      public RetryResult shouldRetry(Exception exception) {
         return before.get();
       }
     };
@@ -134,7 +134,7 @@ public Boolean shouldRetry(Exception exception) {
     assertFalse(handler.shouldRetry(new RuntimeException()));
     assertFalse(handler.shouldRetry(new NullPointerException()));
 
-    before.set(true);
+    before.set(RetryResult.RETRY);
     assertTrue(handler.shouldRetry(new IOException()));
     assertTrue(handler.shouldRetry(new ClosedByInterruptException()));
     assertTrue(handler.shouldRetry(new InterruptedException()));
diff --git a/src/test/java/com/google/gcloud/RetryHelperTest.java b/src/test/java/com/google/gcloud/RetryHelperTest.java
index 565d5680df96..257dc892d229 100644
--- a/src/test/java/com/google/gcloud/RetryHelperTest.java
+++ b/src/test/java/com/google/gcloud/RetryHelperTest.java
@@ -68,10 +68,17 @@ public void testTriesWithExceptionHandling() {
     }
     assertNull(RetryHelper.getContext());
 
-    @SuppressWarnings("serial") class E1 extends Exception {}
-    @SuppressWarnings("serial") class E2 extends E1 {}
-    @SuppressWarnings("serial") class E3 extends E1 {}
-    @SuppressWarnings("serial") class E4 extends E2 {}
+    @SuppressWarnings("serial")
+    class E1 extends Exception {}
+
+    @SuppressWarnings("serial")
+    class E2 extends E1 {}
+
+    @SuppressWarnings("serial")
+    class E3 extends E1 {}
+
+    @SuppressWarnings("serial")
+    class E4 extends E2 {}
 
     params = RetryParams.builder().initialRetryDelayMillis(0).retryMaxAttempts(5).build();
     handler = ExceptionHandler.builder().retryOn(E1.class, E4.class).abortOn(E3.class).build();
@@ -253,4 +260,4 @@ public Integer call() throws IOException {
     });
   }
 
-}
\ No newline at end of file
+}
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
index a693446afc1e..dd655f884159 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
@@ -335,6 +335,10 @@ public void testRunGqlQueryNoCasting() throws DatastoreException {
     responsePbBuilder.getBatchBuilder()
         .setEntityResultType(DatastoreV1.EntityResult.ResultType.PROJECTION).build();
     Datastore mockDatastore = EasyMock.createMock(Datastore.class);
+    DatastoreV1.EntityResult found =
+        DatastoreV1.EntityResult.newBuilder().setEntity(ENTITY1.toPb()).build();
+    EasyMock.expect(mockDatastore.lookup(EasyMock.anyObject()))
+        .andReturn(DatastoreV1.LookupResponse.newBuilder().addFound(found).build());
     EasyMock.expect(mockDatastore.runQuery(requestPb.build())).andReturn(responsePbBuilder.build());
     EasyMock.replay(mockDatastore);
     datastore = DatastoreServiceFactory.getDefault(
@@ -414,6 +418,10 @@ public void testRunStructuredQuery() throws DatastoreException {
     responsePbBuilder.getBatchBuilder()
         .setEntityResultType(DatastoreV1.EntityResult.ResultType.PROJECTION).build();
     Datastore mockDatastore = EasyMock.createMock(Datastore.class);
+    DatastoreV1.EntityResult missing =
+        DatastoreV1.EntityResult.newBuilder().setEntity(ENTITY1.toPb()).build();
+    EasyMock.expect(mockDatastore.lookup(EasyMock.anyObject()))
+        .andReturn(DatastoreV1.LookupResponse.newBuilder().addMissing(missing).build());
     EasyMock.expect(mockDatastore.runQuery(requestPb.build())).andReturn(responsePbBuilder.build());
     EasyMock.replay(mockDatastore);
     datastore = DatastoreServiceFactory.getDefault(

From 938b04db4a2e034b976ee52abd241f51fa001ca4 Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Tue, 30 Dec 2014 23:38:56 -0800
Subject: [PATCH 074/771] Make maven happy

---
 .checkstyle                                        |  2 +-
 .project                                           |  8 ++++----
 .settings/org.eclipse.jdt.core.prefs               |  3 +++
 pom.xml                                            |  2 +-
 .../java/com/google/gcloud/ExceptionHandler.java   | 14 +++++++-------
 5 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/.checkstyle b/.checkstyle
index 429677b11adc..5783bc0d77a1 100644
--- a/.checkstyle
+++ b/.checkstyle
@@ -1,7 +1,7 @@
 
 
 
-  
+  
     
   
 
diff --git a/.project b/.project
index 0443050b010d..89b7470af643 100644
--- a/.project
+++ b/.project
@@ -31,22 +31,22 @@
 			
 		
 		
-			org.eclipse.m2e.core.maven2Builder
+			org.eclipse.wst.validation.validationbuilder
 			
 			
 		
 		
-			org.eclipse.wst.validation.validationbuilder
+			ntut.csie.rleht.builder.RLBuilder
 			
 			
 		
 		
-			ntut.csie.rleht.builder.RLBuilder
+			ch.acanda.eclipse.pmd.builder.PMDBuilder
 			
 			
 		
 		
-			ch.acanda.eclipse.pmd.builder.PMDBuilder
+			org.eclipse.m2e.core.maven2Builder
 			
 			
 		
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index 62f72418e536..83d343fa7442 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -8,9 +8,11 @@ org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonN
 org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
 org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
 org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
 org.eclipse.jdt.core.compiler.compliance=1.7
 org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
 org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
 org.eclipse.jdt.core.compiler.problem.deadCode=warning
@@ -19,6 +21,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
 org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
diff --git a/pom.xml b/pom.xml
index cccce5ebbadf..4a01c702f696 100644
--- a/pom.xml
+++ b/pom.xml
@@ -108,7 +108,7 @@
       
       
         maven-compiler-plugin
-        3.1
+        3.2
         
           1.7
           1.7
diff --git a/src/main/java/com/google/gcloud/ExceptionHandler.java b/src/main/java/com/google/gcloud/ExceptionHandler.java
index 3cb6941d428c..c3b558e58100 100644
--- a/src/main/java/com/google/gcloud/ExceptionHandler.java
+++ b/src/main/java/com/google/gcloud/ExceptionHandler.java
@@ -8,7 +8,6 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
-import com.google.gcloud.ExceptionHandler.Interceptor.RetryResult;
 
 import java.io.Serializable;
 import java.lang.reflect.Method;
@@ -140,10 +139,10 @@ static final class RetryInfo implements Serializable {
 
     private static final long serialVersionUID = -4264634837841455974L;
     private final Class exception;
-    private final RetryResult retry;
+    private final Interceptor.RetryResult retry;
     private final Set children = Sets.newHashSet();
 
-    RetryInfo(Class exception, RetryResult retry) {
+    RetryInfo(Class exception, Interceptor.RetryResult retry) {
       this.exception = checkNotNull(exception);
       this.retry = retry;
     }
@@ -174,10 +173,10 @@ private ExceptionHandler(Builder builder) {
         Sets.intersection(retriableExceptions, nonRetriableExceptions).isEmpty(),
         "Same exception was found in both retriable and non-retriable sets");
     for (Class exception : retriableExceptions) {
-      addToRetryInfos(retryInfos, new RetryInfo(exception, RetryResult.RETRY));
+      addToRetryInfos(retryInfos, new RetryInfo(exception, Interceptor.RetryResult.RETRY));
     }
     for (Class exception : nonRetriableExceptions) {
-      addToRetryInfos(retryInfos,  new RetryInfo(exception, RetryResult.ABORT));
+      addToRetryInfos(retryInfos,  new RetryInfo(exception, Interceptor.RetryResult.ABORT));
     }
   }
 
@@ -242,13 +241,14 @@ public Set> getNonRetriableExceptions() {
 
   boolean shouldRetry(Exception ex) {
     for (Interceptor interceptor : interceptors) {
-      RetryResult retryResult = interceptor.shouldRetry(ex);
+      Interceptor.RetryResult retryResult = interceptor.shouldRetry(ex);
       if (retryResult != null) {
         return retryResult.booleanValue();
       }
     }
     RetryInfo retryInfo = findMostSpecificRetryInfo(retryInfos, ex.getClass());
-    RetryResult retryResult = retryInfo == null ? RetryResult.ABORT : retryInfo.retry;
+    Interceptor.RetryResult retryResult =
+        retryInfo == null ? Interceptor.RetryResult.ABORT : retryInfo.retry;
     for (Interceptor interceptor : interceptors) {
       retryResult = firstNonNull(interceptor.shouldRetry(ex, retryResult), retryResult);
     }

From 823eb9f76aea833253432af4412f403c62e2b9b5 Mon Sep 17 00:00:00 2001
From: aozarov 
Date: Fri, 2 Jan 2015 17:50:22 -0800
Subject: [PATCH 075/771] Automatically invoke gcd for testing

---
 .eclipse-pmd                                  |   2 +-
 full-pmd-ruleset.xml                          |  46 ----
 pmd.xml                                       |   4 +-
 pom.xml                                       |  73 ++++++
 ...a => DatastoreServiceIntegrationTest.java} |  28 ++-
 .../gcloud/datastore/LocalGcdHelper.java      | 232 ++++++++++++++++++
 6 files changed, 324 insertions(+), 61 deletions(-)
 delete mode 100644 full-pmd-ruleset.xml
 rename src/test/java/com/google/gcloud/datastore/{DatastoreServiceTest.java => DatastoreServiceIntegrationTest.java} (97%)
 create mode 100644 src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java

diff --git a/.eclipse-pmd b/.eclipse-pmd
index c14648afb674..5b4a4cb75170 100644
--- a/.eclipse-pmd
+++ b/.eclipse-pmd
@@ -2,6 +2,6 @@
 
   
   
-    
+    
   
 
\ No newline at end of file
diff --git a/full-pmd-ruleset.xml b/full-pmd-ruleset.xml
deleted file mode 100644
index 9f1ec73cc736..000000000000
--- a/full-pmd-ruleset.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-   Full 5.1.1 PMD rule set
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-   
-
diff --git a/pmd.xml b/pmd.xml
index 0b206c082bb3..453c40c8784a 100644
--- a/pmd.xml
+++ b/pmd.xml
@@ -80,7 +80,6 @@
    
    
    
-   
    
    
    
@@ -183,7 +182,6 @@
    
    
    
-   
    
    
    
@@ -333,4 +331,4 @@
    
    
    
-
\ No newline at end of file
+
diff --git a/pom.xml b/pom.xml
index cccce5ebbadf..1dc2f3741739 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,6 +102,79 @@
   
   
     
+      
+        maven-antrun-plugin
+        1.8
+        
+          
+            before-integration-test
+            pre-integration-test
+            
+              
+                
+                
+                  
+                  
+                    
+                  
+                
+              
+            
+            
+              run
+            
+          
+          
+            after-integration-test
+            post-integration-test
+            
+              
+                
+                
+                  
+                  
+                    
+                  
+                
+              
+            
+            
+              run
+            
+          
+        
+      
+      
+        org.apache.maven.plugins
+        maven-surefire-plugin
+        2.18.1
+        
+          
+            **/*IntegrationTest.java
+          
+        
+      
+      
+        org.apache.maven.plugins
+        maven-failsafe-plugin
+        2.18.1
+        
+          
+            none
+          
+          
+            **/*IntegrationTest.java
+          
+        
+        
+          
+            
+              integration-test
+              verify
+            
+          
+        
+      
       
         maven-jar-plugin
         2.5
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
similarity index 97%
rename from src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
rename to src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
index dd655f884159..de43c169c827 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
@@ -17,11 +17,13 @@
 import com.google.gcloud.datastore.StructuredQuery.PropertyFilter;
 
 import org.easymock.EasyMock;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.IOException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -29,9 +31,9 @@
 import java.util.Map;
 
 @RunWith(JUnit4.class)
-public class DatastoreServiceTest {
+public class DatastoreServiceIntegrationTest {
 
-  private static final String DATASET = "dataset1";
+  private static final String DATASET = LocalGcdHelper.DEFAULT_DATASET;
   private static final String KIND1 = "kind1";
   private static final String KIND2 = "kind2";
   private static final NullValue NULL_VALUE = NullValue.of();
@@ -70,19 +72,16 @@ public class DatastoreServiceTest {
   private DatastoreServiceOptions options;
   private DatastoreService datastore;
   private DatastoreHelper helper;
+  private LocalGcdHelper gcdHelper;
 
   @Before
-  public void setUp() {
-    // TODO(ozarov): document that this test depends on a local gcd running.
-    // Unfortunately, the gcd tool is not bundled with the cloud SDK and need
-    // to be downloaded independently from
-    // https://cloud.google.com/datastore/docs/tools/devserver (b/16372095).
-    // To start the gcd run:
-    // gcd.sh create dataset1; gcd.sh start dataset1
-    // We should have an option to start the gcd from maven/ant.
+  public void setUp() throws IOException, InterruptedException {
+    if (!LocalGcdHelper.isActive(DATASET)) {
+      gcdHelper = LocalGcdHelper.start(DATASET);
+    }
     options = DatastoreServiceOptions.builder()
         .dataset(DATASET)
-        .host("http://localhost:8080")
+        .host("http://localhost:" + LocalGcdHelper.PORT)
         .build();
     datastore = DatastoreServiceFactory.getDefault(options);
     helper = DatastoreHelper.createFor(datastore);
@@ -91,6 +90,13 @@ public void setUp() {
     datastore.add(ENTITY1, ENTITY2);
   }
 
+  @After
+  public void tearDown() throws IOException, InterruptedException {
+    if (gcdHelper != null) {
+      gcdHelper.stop();
+    }
+  }
+
   @Test
   public void testGetOptions() {
     assertSame(options, datastore.options());
diff --git a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
new file mode 100644
index 000000000000..dd19a64314ca
--- /dev/null
+++ b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
@@ -0,0 +1,232 @@
+package com.google.gcloud.datastore;
+
+import com.google.api.client.util.Strings;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * Utility to start and stop local Google Cloud Datastore process.
+ */
+public class LocalGcdHelper {
+
+  private final String dataset;
+  private Path gcdPath;
+  private ProcessStreamReader processReader;
+
+  public static final String DEFAULT_DATASET = "dataset1";
+  public static final int PORT = 8080;
+  private static final String GCD = "gcd-v1beta2-rev1-2.1.1";
+  private static final String GCD_LOC = "/" + GCD + ".zip";
+
+  private static class ProcessStreamReader extends Thread {
+
+    private final Process process;
+    private final BufferedReader reader;
+
+    ProcessStreamReader(Process process, String blockUntil) throws IOException {
+      super("Local GCD InputStream reader");
+      setDaemon(true);
+      this.process = process;
+      reader =  new BufferedReader(new InputStreamReader(process.getInputStream()));
+      if (!Strings.isNullOrEmpty(blockUntil)) {
+        String line;
+        do {
+          line = reader.readLine();
+        } while (line != null && !line.contains(blockUntil));
+      }
+    }
+
+    void terminate() throws InterruptedException, IOException {
+      process.destroy();
+      process.waitFor();
+      reader.close();
+    }
+
+    @Override
+    public void run() {
+      try {
+        while (reader.readLine() != null) {
+          // consume line
+        }
+      } catch (IOException e) {
+        // ignore
+      }
+    }
+
+    public static ProcessStreamReader start(Process process, String blockUntil) throws IOException {
+      ProcessStreamReader thread = new ProcessStreamReader(process, blockUntil);
+      thread.start();
+      return thread;
+    }
+  }
+
+  public LocalGcdHelper(String dataset) {
+    this.dataset = dataset;
+  }
+
+  public void start() throws IOException, InterruptedException {
+    sendQuitRequest();
+    gcdPath = Files.createTempDirectory("gcd");
+    File gcdFolder = gcdPath.toFile();
+    gcdFolder.deleteOnExit();
+
+    try (ZipInputStream zipIn = new ZipInputStream(getClass().getResourceAsStream(GCD_LOC))) {
+      ZipEntry entry = zipIn.getNextEntry();
+      while (entry != null) {
+        File filePath = new File(gcdFolder, entry.getName());
+        if (!entry.isDirectory()) {
+          extractFile(zipIn, filePath);
+        } else {
+          filePath.mkdir();
+        }
+        zipIn.closeEntry();
+        entry = zipIn.getNextEntry();
+      }
+    }
+
+    File datasetFolder = new File(gcdFolder, GCD + "/" + dataset);
+    datasetFolder.delete();
+
+    // TODO: When System.getProperty("os.name").startsWith("Windows") use cmd.exe /c and gcd.cmd
+    Process temp = new ProcessBuilder()
+        .redirectErrorStream(true)
+        .directory(new File(gcdFolder, GCD))
+        .redirectOutput(new File("/dev/null"))
+        .command("sh", "gcd.sh", "create", "-d", dataset, dataset)
+        .start();
+    temp.waitFor();
+
+    temp = new ProcessBuilder()
+        .directory(new File(gcdFolder, GCD))
+        .redirectErrorStream(true)
+        .command("sh", "gcd.sh", "start", "--testing", "--allow_remote_shutdown", dataset)
+        .start();
+    processReader = ProcessStreamReader.start(temp, "Dev App Server is now running");
+  }
+
+  private static void extractFile(ZipInputStream zipIn, File filePath) throws IOException {
+    try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath))) {
+      byte[] bytesIn = new byte[1024];
+      int read = 0;
+      while ((read = zipIn.read(bytesIn)) != -1) {
+        bos.write(bytesIn, 0, read);
+      }
+    }
+  }
+
+  public static void sendQuitRequest() {
+    try {
+      URL url = new URL("http", "localhost", PORT, "/_ah/admin/quit");
+      HttpURLConnection con = (HttpURLConnection) url.openConnection();
+      con.setRequestMethod("POST");
+      con.setDoOutput(true);
+      con.setDoInput(true);
+      OutputStream out = con.getOutputStream();
+      out.write("".getBytes());
+      out.flush();
+      InputStream in = con.getInputStream();
+      while (in.read() != -1) {
+        // consume input
+      }
+    } catch (IOException ignore) {
+      // ignore
+    }
+  }
+
+  public void stop() throws IOException, InterruptedException {
+    sendQuitRequest();
+    if (processReader != null) {
+      processReader.terminate();
+    }
+    if (gcdPath != null) {
+      deleteRecurse(gcdPath);
+      gcdPath = null;
+    }
+  }
+
+  private static void deleteRecurse(Path path) throws IOException {
+    if (path == null || !Files.exists(path)) {
+      return;
+    }
+    Files.walkFileTree(path, new SimpleFileVisitor() {
+
+      @Override
+      public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+        Files.delete(dir);
+        return FileVisitResult.CONTINUE;
+      }
+
+      @Override
+      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+        Files.delete(file);
+        return FileVisitResult.CONTINUE;
+      }
+    });
+  }
+
+  public static LocalGcdHelper start(String dataset) throws IOException, InterruptedException {
+    LocalGcdHelper helper = new LocalGcdHelper(dataset);
+    helper.start();
+    return helper;
+  }
+
+  public static void main(String... args) throws IOException, InterruptedException {
+    if (args.length == 1) {
+      switch (args[0]) {
+        case "START":
+          if (!isActive(DEFAULT_DATASET)) {
+            LocalGcdHelper helper = LocalGcdHelper.start(DEFAULT_DATASET);
+            try (FileWriter writer = new FileWriter(".local_gcd_helper")) {
+              writer.write(helper.gcdPath.toAbsolutePath().toString());
+            }
+          }
+          return;
+        case "STOP":
+          sendQuitRequest();
+          File file = new File(".local_gcd_helper");
+          if (file.exists()) {
+            try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
+              String path = reader.readLine();
+              deleteRecurse(Paths.get(path));
+            }
+          }
+          file.delete();
+          return;
+        default:
+          break;
+      }
+    }
+    throw new RuntimeException("expeting only START | STOP");
+  }
+
+  public static boolean isActive(String dataset) {
+    try {
+      String path = "/datastore/v1beta2/datasets/" + dataset + "/lookup";
+      URL url = new URL("http://localhost:" + PORT + path);
+      try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
+        return "Valid RPC".equals(reader.readLine());
+      }
+    } catch (IOException ex) {
+      return false;
+    }
+  }
+}

From 14abf09f7b7bb7eb8d60481bdc676983779545b6 Mon Sep 17 00:00:00 2001
From: aozarov 
Date: Fri, 2 Jan 2015 18:03:48 -0800
Subject: [PATCH 076/771] minor change

---
 pom.xml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/pom.xml b/pom.xml
index 5ed4ff0898f1..69c2673fe060 100644
--- a/pom.xml
+++ b/pom.xml
@@ -87,6 +87,10 @@
       http://repo.maven.apache.org/maven2
     
   
+  
+    UTF-8
+    UTF-8
+  
   
     
       

From bf4f0351afc8431891f5243a6d627359571e638d Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Fri, 2 Jan 2015 23:45:23 -0800
Subject: [PATCH 077/771] replace sh with bash

---
 src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
index dd19a64314ca..8be2523de0f8 100644
--- a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
+++ b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
@@ -111,14 +111,14 @@ public void start() throws IOException, InterruptedException {
         .redirectErrorStream(true)
         .directory(new File(gcdFolder, GCD))
         .redirectOutput(new File("/dev/null"))
-        .command("sh", "gcd.sh", "create", "-d", dataset, dataset)
+        .command("bash", "gcd.sh", "create", "-d", dataset, dataset)
         .start();
     temp.waitFor();
 
     temp = new ProcessBuilder()
         .directory(new File(gcdFolder, GCD))
         .redirectErrorStream(true)
-        .command("sh", "gcd.sh", "start", "--testing", "--allow_remote_shutdown", dataset)
+        .command("bash", "gcd.sh", "start", "--testing", "--allow_remote_shutdown", dataset)
         .start();
     processReader = ProcessStreamReader.start(temp, "Dev App Server is now running");
   }

From e7ce4ead8e76dc06dfe9dd6f2aa9ea73ad9c3ccc Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Sun, 4 Jan 2015 21:55:37 -0800
Subject: [PATCH 078/771] cleanup

---
 RobustaSettings.xml                           |  2 +-
 pmd.xml                                       | 48 ++++++-------------
 .../java/com/google/gcloud/RetryHelper.java   | 19 ++++----
 .../com/google/gcloud/datastore/Cursor.java   |  4 +-
 .../datastore/DatastoreServiceOptions.java    |  2 +-
 .../gcloud/datastore/TransactionOption.java   |  9 ++--
 .../google/gcloud/datastore/Validator.java    |  5 +-
 .../gcloud/datastore/LocalGcdHelper.java      |  3 +-
 .../gcloud/datastore/SerializationTest.java   | 21 ++++----
 9 files changed, 51 insertions(+), 62 deletions(-)

diff --git a/RobustaSettings.xml b/RobustaSettings.xml
index e957a402fb2a..8c664c57feb7 100644
--- a/RobustaSettings.xml
+++ b/RobustaSettings.xml
@@ -1,2 +1,2 @@
 
-
+
diff --git a/pmd.xml b/pmd.xml
index 453c40c8784a..45b3e65a4cc6 100644
--- a/pmd.xml
+++ b/pmd.xml
@@ -6,22 +6,16 @@
    PMD Plugin preferences rule set
    
    
-   
-   
    
    
    
-   
-   
    
-   
    
    
    
    
    
    
-   
    
    
    
@@ -31,12 +25,9 @@
    
    
    
-   
-   
    
    
    
-   
    
    
    
@@ -59,7 +50,6 @@
    
    
    
-   
    
    
    
@@ -67,7 +57,6 @@
    
    
    
-   
    
    
    
@@ -80,19 +69,24 @@
    
    
    
-   
+   
+	   
+		   
+	   
+   
    
-   
    
    
    
    
    
-   
+   
+	   
+		   
+	   
+   
    
-   
    
-   
    
    
    
@@ -107,7 +101,6 @@
    
    
    
-   
    
    
    
@@ -149,10 +142,8 @@
    
    
    
-   
    
    
-   
    
    
    
@@ -171,25 +162,20 @@
    
    
    
-   
    
    
-   
    
    
    
    
-   
    
    
    
    
    
-   
    
    
    
-   
    
    
    
@@ -222,9 +208,7 @@
    
    
    
-   
    
-   
    
    
    
@@ -243,10 +227,13 @@
    
    
    
-   
+   
+	   
+		   
+	   
+   
    
    
-   
    
    
    
@@ -276,9 +263,6 @@
    
    
    
-   
-   
-   
    
    
    
@@ -312,7 +296,6 @@
    
    
    
-   
    
    
    
@@ -328,7 +311,6 @@
    
    
    
-   
    
    
 
diff --git a/src/main/java/com/google/gcloud/RetryHelper.java b/src/main/java/com/google/gcloud/RetryHelper.java
index ed0fcc1dab85..5e4445361227 100644
--- a/src/main/java/com/google/gcloud/RetryHelper.java
+++ b/src/main/java/com/google/gcloud/RetryHelper.java
@@ -18,9 +18,9 @@
 import java.util.logging.Logger;
 
 /**
- * Utility class for retrying operations. For more details about the parameters, see
- * {@link RetryParams}. If the request is never successful, a {@link RetriesExhaustedException} will
- * be thrown.
+ * Utility class for retrying operations.
+ * For more details about the parameters, see {@link RetryParams}.
+ * If the request is never successful, a {@link RetriesExhaustedException} will be thrown.
  *
  * @param  return value of the closure that is being run with retries
  */
@@ -99,8 +99,8 @@ public static final class NonRetriableException extends RetryHelperException {
 
     private static final long serialVersionUID = -2331878521983499652L;
 
-    NonRetriableException(Throwable ex) {
-      super(ex);
+    NonRetriableException(Throwable throwable) {
+      super(throwable);
     }
   }
 
@@ -164,12 +164,13 @@ private V doRetry() throws RetryHelperException {
           log.fine(this + ": attempt #" + attemptNumber + " succeeded");
         }
         return value;
+      } catch (InterruptedException | InterruptedIOException | ClosedByInterruptException e) {
+        if (!exceptionHandler.shouldRetry(e)) {
+          RetryInterruptedException.propagate();
+        }
+        exception = e;
       } catch (Exception e) {
         if (!exceptionHandler.shouldRetry(e)) {
-          if (e instanceof InterruptedException || e instanceof InterruptedIOException
-              || e instanceof ClosedByInterruptException) {
-            RetryInterruptedException.propagate();
-          }
           throw new NonRetriableException(e);
         }
         exception = e;
diff --git a/src/main/java/com/google/gcloud/datastore/Cursor.java b/src/main/java/com/google/gcloud/datastore/Cursor.java
index e95f25144c12..bffdfd3dd9ac 100644
--- a/src/main/java/com/google/gcloud/datastore/Cursor.java
+++ b/src/main/java/com/google/gcloud/datastore/Cursor.java
@@ -65,7 +65,7 @@ public String toUrlSafe() {
     try {
       return URLEncoder.encode(toPb().toString(), UTF_8.name());
     } catch (UnsupportedEncodingException e) {
-      throw new RuntimeException("Unxpeced encoding exception", e);
+      throw new IllegalStateException("Unxpeced encoding exception", e);
     }
   }
 
@@ -77,7 +77,7 @@ public static Cursor fromUrlSafe(String urlSafe) {
       String utf8Str = URLDecoder.decode(urlSafe, UTF_8.name());
       return fromPb(DatastoreV1.Value.parseFrom(utf8Str.getBytes()));
     } catch (UnsupportedEncodingException | InvalidProtocolBufferException e) {
-      throw new RuntimeException("Unxpeced decoding exception", e);
+      throw new IllegalStateException("Unxpeced decoding exception", e);
     }
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java
index f3cc48d69801..009b663ab874 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java
@@ -35,7 +35,7 @@ public static class Builder extends ServiceOptions.Builder {
 
     private String dataset;
     private String namespace;
-    private boolean force = false;
+    private boolean force;
     private Datastore datastore;
 
     private Builder() {
diff --git a/src/main/java/com/google/gcloud/datastore/TransactionOption.java b/src/main/java/com/google/gcloud/datastore/TransactionOption.java
index 597715df20a8..9de8ea8ff745 100644
--- a/src/main/java/com/google/gcloud/datastore/TransactionOption.java
+++ b/src/main/java/com/google/gcloud/datastore/TransactionOption.java
@@ -61,6 +61,11 @@ public IsolationLevel(Level level) {
     public Level level() {
       return level;
     }
+
+    @Override
+    BatchWriteOption toBatchWriteOption() {
+      return null;
+    }
   }
 
   TransactionOption() {
@@ -89,7 +94,5 @@ static Map, TransactionOption> asImmutableMap
     return builder.build();
   }
 
-  BatchWriteOption toBatchWriteOption() {
-    return null;
-  }
+  abstract BatchWriteOption toBatchWriteOption();
 }
diff --git a/src/main/java/com/google/gcloud/datastore/Validator.java b/src/main/java/com/google/gcloud/datastore/Validator.java
index 2f915ccc3d93..235c81b77f16 100644
--- a/src/main/java/com/google/gcloud/datastore/Validator.java
+++ b/src/main/java/com/google/gcloud/datastore/Validator.java
@@ -9,7 +9,7 @@
 /**
  * Utility to validate Datastore type/values.
  */
-class Validator {
+final class Validator {
 
   private static final Pattern DATASET_PATTERN = Pattern.compile(
       "([a-z\\d\\-]{1,100}~)?([a-z\\d][a-z\\d\\-\\.]{0,99}\\:)?([a-z\\d][a-z\\d\\-]{0,99})");
@@ -17,6 +17,9 @@ class Validator {
   private static final Pattern NAMESPACE_PATTERN =
       Pattern.compile(String.format("[0-9A-Za-z\\._\\-]{0,%d}", MAX_NAMESPACE_LENGTH));
 
+  private Validator() {
+    // utility class
+  }
 
   static String validateDataset(String dataset) {
     checkArgument(!Strings.isNullOrEmpty(dataset), "dataset can't be empty or null");
diff --git a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
index 8be2523de0f8..a4f1ed1ff01f 100644
--- a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
+++ b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
@@ -106,7 +106,7 @@ public void start() throws IOException, InterruptedException {
     File datasetFolder = new File(gcdFolder, GCD + "/" + dataset);
     datasetFolder.delete();
 
-    // TODO: When System.getProperty("os.name").startsWith("Windows") use cmd.exe /c and gcd.cmd
+    // TODO: if System.getProperty("os.name").startsWith("Windows") use cmd.exe /c and gcd.cmd
     Process temp = new ProcessBuilder()
         .redirectErrorStream(true)
         .directory(new File(gcdFolder, GCD))
@@ -159,7 +159,6 @@ public void stop() throws IOException, InterruptedException {
     }
     if (gcdPath != null) {
       deleteRecurse(gcdPath);
-      gcdPath = null;
     }
   }
 
diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
index 636ab831c8ac..d87b64d7f480 100644
--- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java
+++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
@@ -17,6 +17,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 
@@ -97,7 +98,8 @@ public class SerializationTest {
   private static final ProjectionEntity PROJECTION_ENTITY = ProjectionEntity.fromPb(ENTITY1.toPb());
 
   @SuppressWarnings("rawtypes")
-  private Multimap typeToValues = ImmutableMultimap.builder()
+  private static final Multimap TYPE_TO_VALUES =
+      ImmutableMultimap.builder()
       .put(Type.NULL, NULL_VALUE)
       .put(Type.KEY, KEY_VALUE)
       .put(Type.STRING, STRING_VALUE)
@@ -115,7 +117,7 @@ public class SerializationTest {
   @Test
   public void testValues() throws Exception {
     for (Type type : Type.values()) {
-      for (Value value : typeToValues.get(type)) {
+      for (Value value : TYPE_TO_VALUES.get(type)) {
         Value copy = serialiazeAndDeserialize(value);
         assertEquals(value, value);
         assertEquals(value, copy);
@@ -141,15 +143,14 @@ public void testTypes() throws Exception {
   }
 
   @SuppressWarnings("unchecked")
-  private  T serialiazeAndDeserialize(T obj) throws Exception {
-    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-    try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {
-      objectOutputStream.writeObject(obj);
+  private  T serialiazeAndDeserialize(T obj) throws IOException, ClassNotFoundException {
+    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+    try (ObjectOutputStream output = new ObjectOutputStream(bytes)) {
+      output.writeObject(obj);
     }
-    byte[] bytes = byteArrayOutputStream.toByteArray();
-    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
-    try (ObjectInputStream in = new ObjectInputStream(byteArrayInputStream)) {
-      return (T) in.readObject();
+    try (ObjectInputStream input =
+        new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()))) {
+      return (T) input.readObject();
     }
   }
 }

From 6019e60c7fe225b32a95d91848afbcc6dc22a09c Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Mon, 5 Jan 2015 13:23:29 -0800
Subject: [PATCH 079/771] more cleanup

---
 .checkstyle                                   |  6 ++--
 pmd.xml                                       | 30 +++++--------------
 .../com/google/gcloud/ExceptionHandler.java   |  2 +-
 .../google/gcloud/datastore/BatchWriter.java  |  4 +--
 .../gcloud/datastore/BatchWriterImpl.java     |  9 +++---
 .../datastore/DatastoreServiceException.java  |  2 +-
 .../com/google/gcloud/datastore/GqlQuery.java |  4 +--
 .../java/com/google/gcloud/datastore/Key.java |  8 ++---
 .../google/gcloud/datastore/PartialKey.java   |  2 +-
 .../google/gcloud/datastore/Serializable.java | 12 ++++----
 .../gcloud/datastore/StructuredQuery.java     |  8 ++---
 .../java/com/google/gcloud/storage/Acl.java   |  2 +-
 .../DatastoreServiceIntegrationTest.java      |  2 --
 13 files changed, 37 insertions(+), 54 deletions(-)

diff --git a/.checkstyle b/.checkstyle
index 5783bc0d77a1..2ad23d5136f6 100644
--- a/.checkstyle
+++ b/.checkstyle
@@ -1,7 +1,7 @@
 
 
-
-  
-    
+
+  
+    
   
 
diff --git a/pmd.xml b/pmd.xml
index 45b3e65a4cc6..62a87e6d063a 100644
--- a/pmd.xml
+++ b/pmd.xml
@@ -3,7 +3,8 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          name="pmd"
          xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
-   PMD Plugin preferences rule set
+	 PMD Plugin preferences rule set
+	 .*/test/.*
    
    
    
@@ -27,7 +28,6 @@
    
    
    
-   
    
    
    
@@ -69,23 +69,11 @@
    
    
    
-   
-	   
-		   
-	   
-   
    
    
    
    
    
-   
-   
-	   
-		   
-	   
-   
-   
    
    
    
@@ -103,6 +91,11 @@
    
    
    
+   
+	   
+		   
+	   
+   
    
    
    
@@ -127,7 +120,6 @@
    
    
    
-   
    
    
    
@@ -165,7 +157,6 @@
    
    
    
-   
    
    
    
@@ -185,10 +176,7 @@
    
    
    
-   
    
-   
-   
    
    
    
@@ -233,7 +221,6 @@
 	   
    
    
-   
    
    
    
@@ -243,7 +230,6 @@
    
    
    
-   
    
    
    
@@ -306,9 +292,7 @@
    
    
    
-   
    
-   
    
    
    
diff --git a/src/main/java/com/google/gcloud/ExceptionHandler.java b/src/main/java/com/google/gcloud/ExceptionHandler.java
index c3b558e58100..25a012ef061a 100644
--- a/src/main/java/com/google/gcloud/ExceptionHandler.java
+++ b/src/main/java/com/google/gcloud/ExceptionHandler.java
@@ -215,7 +215,7 @@ private static Method getCallableMethod(Class clazz) {
       return getCallableMethod(clazz.getSuperclass());
     } catch (SecurityException e) {
       // This should never happen
-      throw new RuntimeException("Unexpected exception", e);
+      throw new IllegalStateException("Unexpected exception", e);
     }
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriter.java b/src/main/java/com/google/gcloud/datastore/BatchWriter.java
index 9fadd9eca6a9..d54bc0d51086 100644
--- a/src/main/java/com/google/gcloud/datastore/BatchWriter.java
+++ b/src/main/java/com/google/gcloud/datastore/BatchWriter.java
@@ -45,7 +45,7 @@ public interface BatchWriter extends DatastoreWriter {
    * @throws DatastoreServiceException if batch is no longer active
    */
   @Override
-  public void delete(Key... key);
+  void delete(Key... key);
 
   /**
    * {@inheritDoc}
@@ -53,7 +53,7 @@ public interface BatchWriter extends DatastoreWriter {
    * @throws DatastoreServiceException if batch is no longer active
    */
   @Override
-  public void put(Entity... entity);
+  void put(Entity... entity);
 
   /**
    * Submit the batch to the Datastore.
diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
index 3589fe3fec24..46713cc1d4a6 100644
--- a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
@@ -8,13 +8,14 @@
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.Map;
+import java.util.Set;
 
 class BatchWriterImpl implements BatchWriter {
 
-  private final LinkedHashMap toAdd = new LinkedHashMap<>();
-  private final LinkedHashMap toUpdate = new LinkedHashMap<>();
-  private final LinkedHashMap toPut = new LinkedHashMap<>();
-  private final LinkedHashSet toDelete = new LinkedHashSet<>();
+  private final Map toAdd = new LinkedHashMap<>();
+  private final Map toUpdate = new LinkedHashMap<>();
+  private final Map toPut = new LinkedHashMap<>();
+  private final Set toDelete = new LinkedHashSet<>();
   private final boolean force;
   protected final DatastoreServiceImpl datastore;
 
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java
index dfb3c56cd262..863a8cddb6e3 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java
@@ -118,7 +118,7 @@ static DatastoreServiceException translateAndThrow(DatastoreException exception)
         JSONObject error = json.getJSONObject("error").getJSONArray("errors").getJSONObject(0);
         reason = error.getString("reason");
         message = error.getString("message");
-      } catch (JSONException ex) {
+      } catch (JSONException ignore) {
         // ignore - will be converted to unknown
       }
     }
diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
index 8569bae8c674..fdb20fdf8178 100644
--- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
@@ -146,8 +146,8 @@ public static final class Builder {
     private String namespace;
     private String queryString;
     private boolean allowLiteral;
-    private Map namedBindings = new TreeMap<>();
-    private List positionalBindings = new LinkedList<>();
+    private final Map namedBindings = new TreeMap<>();
+    private final List positionalBindings = new LinkedList<>();
 
     Builder(Type type, String query) {
       this.type = checkNotNull(type);
diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java
index 113802a8ba5b..551bdce23123 100644
--- a/src/main/java/com/google/gcloud/datastore/Key.java
+++ b/src/main/java/com/google/gcloud/datastore/Key.java
@@ -114,14 +114,14 @@ public String toUrlSafe() {
     try {
       return URLEncoder.encode(toString(), UTF_8.name());
     } catch (UnsupportedEncodingException e) {
-      throw new RuntimeException("Unxpeced encoding exception", e);
+      throw new IllegalStateException("Unxpeced encoding exception", e);
     }
   }
 
   /**
    * Create a {@code Key} given its URL safe encoded form.
    *
-   * @throws RuntimeException when decoding fails
+   * @throws IllegalArgumentException when decoding fails
    */
   public static Key fromUrlSafe(String urlSafe) {
     try {
@@ -129,9 +129,9 @@ public static Key fromUrlSafe(String urlSafe) {
       DatastoreV1.Key keyPb = DatastoreV1.Key.parseFrom(ByteString.copyFromUtf8(utf8Str));
       return fromPb(keyPb);
     } catch (UnsupportedEncodingException e) {
-      throw new RuntimeException("Unxpeced decoding exception", e);
+      throw new IllegalStateException("Unxpeced decoding exception", e);
     } catch (InvalidProtocolBufferException e) {
-      throw new RuntimeException("Could not parse key", e);
+      throw new IllegalArgumentException("Could not parse key", e);
     }
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java
index e819aab8ec0b..fa28ed906e2f 100644
--- a/src/main/java/com/google/gcloud/datastore/PartialKey.java
+++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java
@@ -65,7 +65,7 @@ static PartialKey fromPb(DatastoreV1.Key keyPb) {
       }
     }
     List pathElementsPb = keyPb.getPathElementList();
-    Preconditions.checkArgument(pathElementsPb.size() > 0, "Path must not be empty");
+    Preconditions.checkArgument(!pathElementsPb.isEmpty(), "Path must not be empty");
     ImmutableList.Builder pathBuilder = ImmutableList.builder();
     for (DatastoreV1.Key.PathElement pathElementPb : pathElementsPb) {
       pathBuilder.add(PathElement.fromPb(pathElementPb));
diff --git a/src/main/java/com/google/gcloud/datastore/Serializable.java b/src/main/java/com/google/gcloud/datastore/Serializable.java
index c9f6b7984512..3770ecfccf7f 100644
--- a/src/main/java/com/google/gcloud/datastore/Serializable.java
+++ b/src/main/java/com/google/gcloud/datastore/Serializable.java
@@ -20,14 +20,14 @@ public String toString() {
     return toPb().toString();
   }
 
-  private void writeObject(ObjectOutputStream out) throws IOException {
-    out.defaultWriteObject();
-    out.writeObject(toPb().toByteArray());
+  private void writeObject(ObjectOutputStream output) throws IOException {
+    output.defaultWriteObject();
+    output.writeObject(toPb().toByteArray());
   }
 
-  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
-    in.defaultReadObject();
-    bytesPb = (byte[]) in.readObject();
+  private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
+    input.defaultReadObject();
+    bytesPb = (byte[]) input.readObject();
   }
 
   protected Object readResolve() throws ObjectStreamException {
diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index 84d7d65e2c25..49657064f524 100644
--- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -577,13 +577,13 @@ public static Projection first(String property) {
 
   static class BaseBuilder> {
 
-    private Type type;
+    private final Type type;
     private String namespace;
     private String kind;
-    private List projection = new LinkedList<>();
+    private final List projection = new LinkedList<>();
     private Filter filter;
-    private List groupBy = new LinkedList<>();
-    private List orderBy = new LinkedList<>();
+    private final List groupBy = new LinkedList<>();
+    private final List orderBy = new LinkedList<>();
     private Cursor startCursor;
     private Cursor endCursor;
     private int offset;
diff --git a/src/main/java/com/google/gcloud/storage/Acl.java b/src/main/java/com/google/gcloud/storage/Acl.java
index 13bbe2bfb44d..a0ef42844b7d 100644
--- a/src/main/java/com/google/gcloud/storage/Acl.java
+++ b/src/main/java/com/google/gcloud/storage/Acl.java
@@ -2,7 +2,7 @@
 
 public interface Acl {
 
-  public class ProjectTeam {
+  class ProjectTeam {
       // ProjectNumber: The project number.
   //ProjectNumber string `json:"projectNumber,omitempty"`
 
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
index de43c169c827..93f59598948b 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
@@ -154,7 +154,6 @@ public void testTransactionWithRead() {
       transaction.commit();
       fail("Expecting a failure");
     } catch (DatastoreServiceException expected) {
-      expected.printStackTrace();
       assertEquals(DatastoreServiceException.Code.ABORTED, expected.code());
     }
   }
@@ -181,7 +180,6 @@ public void testTransactionWithQuery() {
       transaction.commit();
       fail("Expecting a failure");
     } catch (DatastoreServiceException expected) {
-      expected.printStackTrace();
       assertEquals(DatastoreServiceException.Code.ABORTED, expected.code());
     }
   }

From ff2827e0d5ba996788e0aa67d8f83f4ac6eac477 Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Mon, 5 Jan 2015 18:14:28 -0800
Subject: [PATCH 080/771] more cleanup

---
 .gitignore                                    |  1 +
 git-demo.iml                                  | 48 +++++++++++++++++++
 .../java/com/google/gcloud/RetryHelper.java   |  4 +-
 .../gcloud/datastore/BatchWriterImpl.java     |  2 +-
 .../com/google/gcloud/datastore/Blob.java     |  7 +--
 .../com/google/gcloud/datastore/Cursor.java   |  9 ++--
 .../gcloud/datastore/DatastoreService.java    | 14 +++---
 .../com/google/gcloud/datastore/DateTime.java |  5 +-
 .../com/google/gcloud/datastore/GqlQuery.java | 14 +++---
 .../java/com/google/gcloud/datastore/Key.java |  4 +-
 .../google/gcloud/datastore/ListValue.java    |  5 +-
 .../gcloud/datastore/PartialEntity.java       |  2 +-
 .../google/gcloud/datastore/QueryResult.java  |  2 +-
 .../gcloud/datastore/StructuredQuery.java     | 13 ++---
 .../gcloud/datastore/TransactionImpl.java     |  2 +-
 .../google/gcloud/datastore/Validator.java    |  2 +-
 .../com/google/gcloud/RetryHelperTest.java    |  3 +-
 .../DatastoreServiceIntegrationTest.java      |  2 +-
 .../gcloud/datastore/LocalGcdHelper.java      |  5 +-
 19 files changed, 88 insertions(+), 56 deletions(-)
 create mode 100644 git-demo.iml

diff --git a/.gitignore b/.gitignore
index 3ae51aeeae93..bb7802aa123c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 /target/
 .settings
+.idea
diff --git a/git-demo.iml b/git-demo.iml
new file mode 100644
index 000000000000..292e56683deb
--- /dev/null
+++ b/git-demo.iml
@@ -0,0 +1,48 @@
+
+
+  
+    
+    
+    
+      
+      
+      
+      
+      
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+  
+
\ No newline at end of file
diff --git a/src/main/java/com/google/gcloud/RetryHelper.java b/src/main/java/com/google/gcloud/RetryHelper.java
index 5e4445361227..fd2bce4411f3 100644
--- a/src/main/java/com/google/gcloud/RetryHelper.java
+++ b/src/main/java/com/google/gcloud/RetryHelper.java
@@ -122,7 +122,7 @@ public int getAttemptNumber() {
   }
 
   @VisibleForTesting
-  static final void setContext(Context ctx) {
+  static void setContext(Context ctx) {
     if (ctx == null) {
       context.remove();
     } else {
@@ -130,7 +130,7 @@ static final void setContext(Context ctx) {
     }
   }
 
-  static final Context getContext() {
+  static Context getContext() {
     return context.get();
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
index 46713cc1d4a6..83cc6cb0808c 100644
--- a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
@@ -34,7 +34,7 @@ class BatchWriterImpl implements BatchWriter {
 
   protected void checkActive() {
     if (!active) {
-      throwInvalidRequest(getName() + " is no longer active");
+      throw throwInvalidRequest(getName() + " is no longer active");
     }
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/Blob.java b/src/main/java/com/google/gcloud/datastore/Blob.java
index f1fabe2f4899..b78dcc6ae922 100644
--- a/src/main/java/com/google/gcloud/datastore/Blob.java
+++ b/src/main/java/com/google/gcloud/datastore/Blob.java
@@ -60,10 +60,7 @@ public boolean equals(Object obj) {
     if (obj == this) {
       return true;
     }
-    if (!(obj instanceof Blob)) {
-      return false;
-    }
-    return byteString.equals(((Blob) obj).byteString);
+    return obj instanceof Blob && byteString.equals(((Blob) obj).byteString);
   }
 
   /**
@@ -113,7 +110,7 @@ public void copyTo(ByteBuffer target) {
   /**
    * Copies bytes into a buffer.
    *
-   * @throws java.io.IndexOutOfBoundsException if an offset or size is negative or too large
+   * @throws IndexOutOfBoundsException if an offset or size is negative or too large
    */
   public void copyTo(byte[] target) {
     byteString.copyTo(target, 0, 0, length());
diff --git a/src/main/java/com/google/gcloud/datastore/Cursor.java b/src/main/java/com/google/gcloud/datastore/Cursor.java
index bffdfd3dd9ac..6f4a0b682ab0 100644
--- a/src/main/java/com/google/gcloud/datastore/Cursor.java
+++ b/src/main/java/com/google/gcloud/datastore/Cursor.java
@@ -38,10 +38,7 @@ public boolean equals(Object obj) {
     if (obj == this) {
       return true;
     }
-    if (!(obj instanceof Cursor)) {
-      return false;
-    }
-    return byteString.equals(((Cursor) obj).byteString);
+    return obj instanceof Cursor && byteString.equals(((Cursor) obj).byteString);
   }
 
   @Override
@@ -65,7 +62,7 @@ public String toUrlSafe() {
     try {
       return URLEncoder.encode(toPb().toString(), UTF_8.name());
     } catch (UnsupportedEncodingException e) {
-      throw new IllegalStateException("Unxpeced encoding exception", e);
+      throw new IllegalStateException("Unexpected encoding exception", e);
     }
   }
 
@@ -77,7 +74,7 @@ public static Cursor fromUrlSafe(String urlSafe) {
       String utf8Str = URLDecoder.decode(urlSafe, UTF_8.name());
       return fromPb(DatastoreV1.Value.parseFrom(utf8Str.getBytes()));
     } catch (UnsupportedEncodingException | InvalidProtocolBufferException e) {
-      throw new IllegalStateException("Unxpeced decoding exception", e);
+      throw new IllegalStateException("Unexpected decoding exception", e);
     }
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java
index f0f84191b18e..48ad05fe5713 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java
@@ -15,7 +15,7 @@ public interface DatastoreService extends DatastoreReaderWriter {
   /**
    * Returns a new Datastore transaction.
    *
-   * @throws DatastoreServiceExcepiton upon failure
+   * @throws DatastoreServiceException upon failure
    */
   Transaction newTransaction(TransactionOption... options);
 
@@ -30,7 +30,7 @@ public interface DatastoreService extends DatastoreReaderWriter {
    * The returned key will have the same information (dataset, kind, namespace and ancestors)
    * as the given key and will have a newly assigned id.
    *
-   * @throws DatastoreServiceExcepiton upon failure
+   * @throws DatastoreServiceException upon failure
    */
   Key allocateId(PartialKey key);
 
@@ -38,34 +38,34 @@ public interface DatastoreService extends DatastoreReaderWriter {
    * Returns a list of keys using the allocated ids ordered by the input.
    *
    * @see #allocateId(PartialKey)
-   * @throws DatastoreServiceExcepiton upon failure
+   * @throws DatastoreServiceException upon failure
    */
   Iterator allocateId(PartialKey key, PartialKey... others);
 
   /**
    * {@inheritDoc}
-   * @throws DatastoreServiceExcepiton upon failure
+   * @throws DatastoreServiceException upon failure
    */
   @Override
   void add(Entity... entity);
 
   /**
    * {@inheritDoc}
-   * @throws DatastoreServiceExcepiton upon failure
+   * @throws DatastoreServiceException upon failure
    */
   @Override
   void update(Entity... entity);
 
   /**
    * {@inheritDoc}
-   * @throws DatastoreServiceExcepiton upon failure
+   * @throws DatastoreServiceException upon failure
    */
   @Override
   void put(Entity... entity);
 
   /**
    * {@inheritDoc}
-   * @throws DatastoreServiceExcepiton upon failure
+   * @throws DatastoreServiceException upon failure
    */
   @Override
   void delete(Key... key);
diff --git a/src/main/java/com/google/gcloud/datastore/DateTime.java b/src/main/java/com/google/gcloud/datastore/DateTime.java
index e04d603ff031..8c9ec1dbdca6 100644
--- a/src/main/java/com/google/gcloud/datastore/DateTime.java
+++ b/src/main/java/com/google/gcloud/datastore/DateTime.java
@@ -42,10 +42,7 @@ public boolean equals(Object obj) {
     if (obj == this) {
       return true;
     }
-    if (!(obj instanceof DateTime)) {
-      return false;
-    }
-    return timestampMicroseconds == ((DateTime) obj).timestampMicroseconds;
+    return obj instanceof DateTime && timestampMicroseconds == ((DateTime) obj).timestampMicroseconds;
   }
 
   public long timestampMicroseconds() {
diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
index fdb20fdf8178..72d3f8c8e5bc 100644
--- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
@@ -27,9 +27,9 @@
  * 

A usage example:

* *

When the type of the results is known the preferred usage would be: - *

 {@code
- *   Query query = GqlQuery.builder(Query.Type.FULL, "select * from kind").build();
- *   QueryResult results = datastore.run(query);
+ * 
{@code
+ *   Query<Entity> query = GqlQuery.builder(Query.Type.FULL, "select * from kind").build();
+ *   QueryResult<Entity> results = datastore.run(query);
  *   while (results.hasNext()) {
  *     Entity entity = results.next();
  *     ...
@@ -37,11 +37,11 @@
  * } 
* *

When the type of the results is unknown you can use this approach: - *

 {@code
- *   Query query = GqlQuery.builder("select __key__ from kind").build();
- *   QueryResult results = datastore.run(query);
+ * 
{@code
+ *   Query<?> query = GqlQuery.builder("select __key__ from kind").build();
+ *   QueryResult<?> results = datastore.run(query);
  *   if (Key.class.isAssignableFrom(results.resultClass())) {
- *     QueryResult keys = (QueryResult) results;
+ *     QueryResult<Key> keys = (QueryResult<Key>) results;
  *     while (keys.hasNext()) {
  *       Key key = keys.next();
  *       ...
diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java
index 551bdce23123..c35efaeb6849 100644
--- a/src/main/java/com/google/gcloud/datastore/Key.java
+++ b/src/main/java/com/google/gcloud/datastore/Key.java
@@ -114,7 +114,7 @@ public String toUrlSafe() {
     try {
       return URLEncoder.encode(toString(), UTF_8.name());
     } catch (UnsupportedEncodingException e) {
-      throw new IllegalStateException("Unxpeced encoding exception", e);
+      throw new IllegalStateException("Unexpected encoding exception", e);
     }
   }
 
@@ -129,7 +129,7 @@ public static Key fromUrlSafe(String urlSafe) {
       DatastoreV1.Key keyPb = DatastoreV1.Key.parseFrom(ByteString.copyFromUtf8(utf8Str));
       return fromPb(keyPb);
     } catch (UnsupportedEncodingException e) {
-      throw new IllegalStateException("Unxpeced decoding exception", e);
+      throw new IllegalStateException("Unexpected decoding exception", e);
     } catch (InvalidProtocolBufferException e) {
       throw new IllegalArgumentException("Could not parse key", e);
     }
diff --git a/src/main/java/com/google/gcloud/datastore/ListValue.java b/src/main/java/com/google/gcloud/datastore/ListValue.java
index 5af230d768b1..5b932c8c1da1 100644
--- a/src/main/java/com/google/gcloud/datastore/ListValue.java
+++ b/src/main/java/com/google/gcloud/datastore/ListValue.java
@@ -69,8 +69,7 @@ public Builder addValue(Value first, Value... other) {
     @Override
     public Builder indexed(boolean indexed) {
       // see b/18704917
-      DatastoreServiceException.throwInvalidRequest("ListValue can't specify index");
-      return this;
+      throw DatastoreServiceException.throwInvalidRequest("ListValue can't specify index");
     }
 
     /**
@@ -80,7 +79,7 @@ public Builder indexed(boolean indexed) {
      */
     @Override
     public Builder set(List> values) {
-      listBuilder = ImmutableList.>builder();
+      listBuilder = ImmutableList.builder();
       for (Value value : values) {
         addValue(value);
       }
diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java
index df520d3f48b9..4f3a71955455 100644
--- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java
+++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java
@@ -52,7 +52,7 @@ protected PartialEntity(PartialKey key, ImmutableSortedMap> pro
    * with the given {@code key}.
    */
   public Entity toEntity(Key key) {
-    return new Entity(key, ImmutableSortedMap.>copyOf(properties()));
+    return new Entity(key, ImmutableSortedMap.copyOf(properties()));
   }
 
   /**
diff --git a/src/main/java/com/google/gcloud/datastore/QueryResult.java b/src/main/java/com/google/gcloud/datastore/QueryResult.java
index 3dab5967e81d..95fb993b1771 100644
--- a/src/main/java/com/google/gcloud/datastore/QueryResult.java
+++ b/src/main/java/com/google/gcloud/datastore/QueryResult.java
@@ -9,7 +9,7 @@
  * Results are loaded lazily therefore it is possible to get a {@code DatastoreServiceException}
  * upon {@link Iterator#hasNext hasNext} or {@link Iterator#next next} calls.
  *
- * @param V the type of the results value.
+ * @param  the type of the results value.
  */
 public interface QueryResult extends Iterator {
 
diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index 49657064f524..7cd197e27ee4 100644
--- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -17,10 +17,7 @@
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import java.io.Serializable;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
 
 /**
  * An implementation of a Google Cloud Datastore Query that can be constructed by providing
@@ -648,9 +645,7 @@ public B orderBy(OrderBy orderBy, OrderBy... others) {
 
     public B addOrderBy(OrderBy orderBy, OrderBy... others) {
       this.orderBy.add(orderBy);
-      for (OrderBy other : others) {
-        this.orderBy.add(other);
-      }
+      Collections.addAll(this.orderBy, others);
       return self();
     }
 
@@ -686,9 +681,7 @@ protected B groupBy(String property, String... others) {
 
     protected B addGroupBy(String property, String... others) {
       this.groupBy.add(property);
-      for (String other : others) {
-        this.groupBy.add(other);
-      }
+      Collections.addAll(this.groupBy, others);
       return self();
     }
 
diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java
index 8b43f421b9e5..0bd51b0e8615 100644
--- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java
@@ -90,7 +90,7 @@ protected String getName() {
   protected void checkActive() {
     super.checkActive();
     if (wasRolledback) {
-      throwInvalidRequest(getName() + " is not active (was rolledback)");
+      throw throwInvalidRequest(getName() + " is not active (was rolledback)");
     }
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/Validator.java b/src/main/java/com/google/gcloud/datastore/Validator.java
index 235c81b77f16..4d4afbc120b4 100644
--- a/src/main/java/com/google/gcloud/datastore/Validator.java
+++ b/src/main/java/com/google/gcloud/datastore/Validator.java
@@ -12,7 +12,7 @@
 final class Validator {
 
   private static final Pattern DATASET_PATTERN = Pattern.compile(
-      "([a-z\\d\\-]{1,100}~)?([a-z\\d][a-z\\d\\-\\.]{0,99}\\:)?([a-z\\d][a-z\\d\\-]{0,99})");
+      "([a-z\\d\\-]{1,100}~)?([a-z\\d][a-z\\d\\-\\.]{0,99}:)?([a-z\\d][a-z\\d\\-]{0,99})");
   private static final int MAX_NAMESPACE_LENGTH = 100;
   private static final Pattern NAMESPACE_PATTERN =
       Pattern.compile(String.format("[0-9A-Za-z\\._\\-]{0,%d}", MAX_NAMESPACE_LENGTH));
diff --git a/src/test/java/com/google/gcloud/RetryHelperTest.java b/src/test/java/com/google/gcloud/RetryHelperTest.java
index 257dc892d229..1fafae35fd50 100644
--- a/src/test/java/com/google/gcloud/RetryHelperTest.java
+++ b/src/test/java/com/google/gcloud/RetryHelperTest.java
@@ -87,8 +87,7 @@ class E4 extends E2 {}
     try {
       RetryHelper.runWithRetries(new Callable() {
         @Override public Void call() throws E1 {
-          E1 exception = exceptions.next();
-          throw exception;
+          throw exceptions.next();
         }
       }, params, handler);
       fail("Exception should have been thrown");
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
index 93f59598948b..1d943abf172d 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
@@ -525,7 +525,7 @@ public void testGetArray() {
     assertEquals(false, entity3.getBoolean("bool"));
     assertEquals(LIST_VALUE2.get(), entity3.getList("list"));
     PartialEntity partial1 = entity3.getEntity("partial1");
-    Entity partial2 = (Entity) entity3.getEntity("partial2");
+    Entity partial2 = entity3.getEntity("partial2");
     assertEquals(partial1, PARTIAL_ENTITY2);
     assertEquals(partial2, ENTITY2);
     assertEquals(Value.Type.BOOLEAN, entity3.getValue("bool").type());
diff --git a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
index a4f1ed1ff01f..519b7ae3ee72 100644
--- a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
+++ b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
@@ -126,7 +126,7 @@ public void start() throws IOException, InterruptedException {
   private static void extractFile(ZipInputStream zipIn, File filePath) throws IOException {
     try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath))) {
       byte[] bytesIn = new byte[1024];
-      int read = 0;
+      int read;
       while ((read = zipIn.read(bytesIn)) != -1) {
         bos.write(bytesIn, 0, read);
       }
@@ -214,9 +214,10 @@ public static void main(String... args) throws IOException, InterruptedException
           break;
       }
     }
-    throw new RuntimeException("expeting only START | STOP");
+    throw new RuntimeException("expecting only START | STOP");
   }
 
+  @SuppressWarnings("BooleanMethodIsAlwaysInverted")
   public static boolean isActive(String dataset) {
     try {
       String path = "/datastore/v1beta2/datasets/" + dataset + "/lookup";

From 7383b8959a5b59c57e8756553774b34138594ee2 Mon Sep 17 00:00:00 2001
From: aozarov 
Date: Tue, 6 Jan 2015 16:04:47 -0800
Subject: [PATCH 081/771] minor fix

---
 .gitignore                                    |   1 +
 git-demo.iml                                  | 201 +++++++++++++++++-
 .../java/com/google/gcloud/RetryHelper.java   |  13 +-
 3 files changed, 206 insertions(+), 9 deletions(-)

diff --git a/.gitignore b/.gitignore
index bb7802aa123c..3aeb1bf01c0e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 /target/
 .settings
 .idea
+*.iml
diff --git a/git-demo.iml b/git-demo.iml
index 292e56683deb..8f8bb491e93c 100644
--- a/git-demo.iml
+++ b/git-demo.iml
@@ -45,4 +45,203 @@
     
     
   
-
\ No newline at end of file
+  
+    
+    
+    
+  
+
diff --git a/src/main/java/com/google/gcloud/RetryHelper.java b/src/main/java/com/google/gcloud/RetryHelper.java
index fd2bce4411f3..4a590e64d0b3 100644
--- a/src/main/java/com/google/gcloud/RetryHelper.java
+++ b/src/main/java/com/google/gcloud/RetryHelper.java
@@ -1,12 +1,5 @@
 package com.google.gcloud;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-import static java.lang.Math.max;
-import static java.lang.Math.min;
-import static java.lang.Math.pow;
-import static java.lang.Math.random;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.MoreObjects.ToStringHelper;
@@ -17,6 +10,10 @@
 import java.util.concurrent.Callable;
 import java.util.logging.Logger;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.Math.*;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
 /**
  * Utility class for retrying operations.
  * For more details about the parameters, see {@link RetryParams}.
@@ -148,7 +145,7 @@ static Context getContext() {
   public String toString() {
     ToStringHelper toStringHelper = MoreObjects.toStringHelper(this);
     toStringHelper.add("stopwatch", stopwatch);
-    toStringHelper.add("attempNumber", attemptNumber);
+    toStringHelper.add("attemptNumber", attemptNumber);
     toStringHelper.add("callable", callable);
     return toStringHelper.toString();
   }

From 941c63e4072c56b4a8614f74481b6af477606960 Mon Sep 17 00:00:00 2001
From: aozarov 
Date: Tue, 6 Jan 2015 16:13:42 -0800
Subject: [PATCH 082/771] revert wrong formatting

---
 src/main/java/com/google/gcloud/RetryHelper.java | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/main/java/com/google/gcloud/RetryHelper.java b/src/main/java/com/google/gcloud/RetryHelper.java
index 4a590e64d0b3..25f58b4b8b79 100644
--- a/src/main/java/com/google/gcloud/RetryHelper.java
+++ b/src/main/java/com/google/gcloud/RetryHelper.java
@@ -1,5 +1,9 @@
 package com.google.gcloud;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.Math.*;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.MoreObjects.ToStringHelper;
@@ -10,10 +14,6 @@
 import java.util.concurrent.Callable;
 import java.util.logging.Logger;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-import static java.lang.Math.*;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-
 /**
  * Utility class for retrying operations.
  * For more details about the parameters, see {@link RetryParams}.
@@ -157,7 +157,7 @@ private V doRetry() throws RetryHelperException {
       Exception exception;
       try {
         V value = callable.call();
-        if (attemptNumber > 1) {
+      if (attemptNumber > 1) {
           log.fine(this + ": attempt #" + attemptNumber + " succeeded");
         }
         return value;

From d3a014d9ac4a19ea1308ce33646fe162d1edb1c3 Mon Sep 17 00:00:00 2001
From: aozarov 
Date: Tue, 6 Jan 2015 16:17:31 -0800
Subject: [PATCH 083/771] revert wrong formatting

---
 src/main/java/com/google/gcloud/RetryHelper.java | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/main/java/com/google/gcloud/RetryHelper.java b/src/main/java/com/google/gcloud/RetryHelper.java
index 25f58b4b8b79..715141a01241 100644
--- a/src/main/java/com/google/gcloud/RetryHelper.java
+++ b/src/main/java/com/google/gcloud/RetryHelper.java
@@ -1,7 +1,10 @@
 package com.google.gcloud;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static java.lang.Math.*;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+import static java.lang.Math.pow;
+import static java.lang.Math.random;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 
 import com.google.common.annotations.VisibleForTesting;

From 1178bb0ffd534c2784464725c79b9a579d9a5930 Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Wed, 7 Jan 2015 22:54:17 -0800
Subject: [PATCH 084/771] even more cleanup

---
 .gitignore                                    |   1 +
 findbugs-exclude.xml                          |   2 +
 git-demo.iml                                  | 201 +++++++++++++++++-
 .../java/com/google/gcloud/AuthConfig.java    |   8 +-
 .../com/google/gcloud/ExceptionHandler.java   |  37 ++--
 .../java/com/google/gcloud/RetryHelper.java   |  16 +-
 .../com/google/gcloud/ServiceOptions.java     |  25 ++-
 .../google/gcloud/datastore/BaseEntity.java   |  38 ++--
 .../com/google/gcloud/datastore/BaseKey.java  |  20 +-
 .../gcloud/datastore/BatchWriterImpl.java     |  18 +-
 .../com/google/gcloud/datastore/Blob.java     |   7 +-
 .../com/google/gcloud/datastore/Cursor.java   |   7 +-
 .../datastore/DatastoreServiceImpl.java       |  24 ++-
 .../datastore/DatastoreServiceOptions.java    |   7 +-
 .../com/google/gcloud/datastore/DateTime.java |   6 +-
 .../com/google/gcloud/datastore/GqlQuery.java |  11 +-
 .../google/gcloud/datastore/PathElement.java  |   9 +-
 .../gcloud/datastore/ProjectionEntity.java    |   6 +-
 .../com/google/gcloud/datastore/Query.java    |   7 +-
 .../gcloud/datastore/QueryResultImpl.java     |   3 +-
 .../gcloud/datastore/StructuredQuery.java     |  38 ++--
 .../gcloud/datastore/TransactionImpl.java     |  10 +-
 .../google/gcloud/datastore/Validator.java    |   8 +-
 .../com/google/gcloud/datastore/Value.java    |   2 +-
 .../gcloud/storage/StorageServiceOptions.java |  22 +-
 .../google/gcloud/ExceptionHandlerTest.java   |  16 +-
 .../com/google/gcloud/RetryHelperTest.java    |  61 +++---
 .../DatastoreServiceIntegrationTest.java      |  12 +-
 .../gcloud/datastore/LocalGcdHelper.java      |  17 +-
 .../gcloud/datastore/SerializationTest.java   |  10 +-
 30 files changed, 439 insertions(+), 210 deletions(-)
 create mode 100644 findbugs-exclude.xml

diff --git a/.gitignore b/.gitignore
index bb7802aa123c..1188335c97d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 /target/
 .settings
 .idea
+.DS_Store
diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml
new file mode 100644
index 000000000000..43bc3321deb5
--- /dev/null
+++ b/findbugs-exclude.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/git-demo.iml b/git-demo.iml
index 292e56683deb..b5c10cc72045 100644
--- a/git-demo.iml
+++ b/git-demo.iml
@@ -10,7 +10,7 @@
       
       
     
-    
+    
     
     
     
@@ -45,4 +45,203 @@
     
     
   
+  
+    
+    
+    
+  
 
\ No newline at end of file
diff --git a/src/main/java/com/google/gcloud/AuthConfig.java b/src/main/java/com/google/gcloud/AuthConfig.java
index 67e7b572c133..e1c5646c66d9 100644
--- a/src/main/java/com/google/gcloud/AuthConfig.java
+++ b/src/main/java/com/google/gcloud/AuthConfig.java
@@ -57,9 +57,8 @@ protected HttpRequestInitializer httpRequestInitializer(
     }
   }
 
-  protected abstract HttpRequestInitializer httpRequestInitializer(
-      HttpTransport transport, Set scopes);
-
+  protected abstract HttpRequestInitializer httpRequestInitializer(HttpTransport transport,
+      Set scopes);
 
   public static AuthConfig createForAppEngine() {
     return new AppEngineAuthConfig();
@@ -69,7 +68,8 @@ public static AuthConfig createForComputeEngine() throws IOException, GeneralSec
     final ComputeCredential cred = getComputeCredential();
     return new AuthConfig() {
       @Override
-      protected HttpRequestInitializer httpRequestInitializer(HttpTransport ts, Set sc) {
+      protected HttpRequestInitializer httpRequestInitializer(HttpTransport transport,
+          Set scopes) {
         return cred;
       }
     };
diff --git a/src/main/java/com/google/gcloud/ExceptionHandler.java b/src/main/java/com/google/gcloud/ExceptionHandler.java
index 25a012ef061a..7330a84ba06d 100644
--- a/src/main/java/com/google/gcloud/ExceptionHandler.java
+++ b/src/main/java/com/google/gcloud/ExceptionHandler.java
@@ -27,7 +27,7 @@ public final class ExceptionHandler implements Serializable {
   private final ImmutableList interceptors;
   private final ImmutableSet> retriableExceptions;
   private final ImmutableSet> nonRetriableExceptions;
-  private final Set retryInfos = Sets.newHashSet();
+  private final Set retryInfo = Sets.newHashSet();
 
   public interface Interceptor extends Serializable {
 
@@ -38,7 +38,7 @@ enum RetryResult {
 
       private final boolean booleanValue;
 
-      private RetryResult(boolean booleanValue) {
+      RetryResult(boolean booleanValue) {
         this.booleanValue = booleanValue;
       }
 
@@ -55,7 +55,7 @@ boolean booleanValue() {
      *     ({@link RetryResult#RETRY}), propagated ({@link RetryResult#ABORT}),
      *     or evaluation should proceed ({@code null}).
      */
-    RetryResult shouldRetry(Exception exception);
+    RetryResult beforeEval(Exception exception);
 
     /**
      * This method is called after the evaluation and could alter its result.
@@ -66,7 +66,7 @@ boolean booleanValue() {
      *     ({@link RetryResult#RETRY}), propagated ({@link RetryResult#ABORT}),
      *     or evaluation should proceed ({@code null}).
      */
-    RetryResult shouldRetry(Exception exception, RetryResult retryResult);
+    RetryResult afterEval(Exception exception, RetryResult retryResult);
   }
 
   /**
@@ -173,31 +173,30 @@ private ExceptionHandler(Builder builder) {
         Sets.intersection(retriableExceptions, nonRetriableExceptions).isEmpty(),
         "Same exception was found in both retriable and non-retriable sets");
     for (Class exception : retriableExceptions) {
-      addToRetryInfos(retryInfos, new RetryInfo(exception, Interceptor.RetryResult.RETRY));
+      addRetryInfo(new RetryInfo(exception, Interceptor.RetryResult.RETRY), retryInfo);
     }
     for (Class exception : nonRetriableExceptions) {
-      addToRetryInfos(retryInfos,  new RetryInfo(exception, Interceptor.RetryResult.ABORT));
+      addRetryInfo(new RetryInfo(exception, Interceptor.RetryResult.ABORT), retryInfo);
     }
   }
 
-  private static void addToRetryInfos(Set retryInfos, RetryInfo retryInfo) {
-    for (RetryInfo current : retryInfos) {
+  private static void addRetryInfo(RetryInfo retryInfo, Set dest) {
+    for (RetryInfo current : dest) {
       if (current.exception.isAssignableFrom(retryInfo.exception)) {
-        addToRetryInfos(current.children, retryInfo);
+        addRetryInfo(retryInfo, current.children);
         return;
       }
       if (retryInfo.exception.isAssignableFrom(current.exception)) {
         retryInfo.children.add(current);
       }
     }
-    retryInfos.removeAll(retryInfo.children);
-    retryInfos.add(retryInfo);
+    dest.removeAll(retryInfo.children);
+    dest.add(retryInfo);
   }
 
-
-  private static RetryInfo findMostSpecificRetryInfo(Set retryInfos,
+  private static RetryInfo findMostSpecificRetryInfo(Set retryInfo,
       Class exception) {
-    for (RetryInfo current : retryInfos) {
+    for (RetryInfo current : retryInfo) {
       if (current.exception.isAssignableFrom(exception)) {
         RetryInfo  match = findMostSpecificRetryInfo(current.children, exception);
         return match == null ? current : match;
@@ -223,10 +222,10 @@ void verifyCaller(Callable callable) {
     Method callMethod = getCallableMethod(callable.getClass());
     for (Class exceptionOrError : callMethod.getExceptionTypes()) {
       Preconditions.checkArgument(Exception.class.isAssignableFrom(exceptionOrError),
-          "Callable method exceptions must be dervied from Exception");
+          "Callable method exceptions must be derived from Exception");
       @SuppressWarnings("unchecked") Class exception =
           (Class) exceptionOrError;
-      Preconditions.checkArgument(findMostSpecificRetryInfo(retryInfos, exception) != null,
+      Preconditions.checkArgument(findMostSpecificRetryInfo(retryInfo, exception) != null,
           "Declared exception '" + exception + "' is not covered by exception handler");
     }
   }
@@ -241,16 +240,16 @@ public Set> getNonRetriableExceptions() {
 
   boolean shouldRetry(Exception ex) {
     for (Interceptor interceptor : interceptors) {
-      Interceptor.RetryResult retryResult = interceptor.shouldRetry(ex);
+      Interceptor.RetryResult retryResult = interceptor.beforeEval(ex);
       if (retryResult != null) {
         return retryResult.booleanValue();
       }
     }
-    RetryInfo retryInfo = findMostSpecificRetryInfo(retryInfos, ex.getClass());
+    RetryInfo retryInfo = findMostSpecificRetryInfo(this.retryInfo, ex.getClass());
     Interceptor.RetryResult retryResult =
         retryInfo == null ? Interceptor.RetryResult.ABORT : retryInfo.retry;
     for (Interceptor interceptor : interceptors) {
-      retryResult = firstNonNull(interceptor.shouldRetry(ex, retryResult), retryResult);
+      retryResult = firstNonNull(interceptor.afterEval(ex, retryResult), retryResult);
     }
     return retryResult.booleanValue();
   }
diff --git a/src/main/java/com/google/gcloud/RetryHelper.java b/src/main/java/com/google/gcloud/RetryHelper.java
index fd2bce4411f3..3c6e23cf085c 100644
--- a/src/main/java/com/google/gcloud/RetryHelper.java
+++ b/src/main/java/com/google/gcloud/RetryHelper.java
@@ -15,6 +15,7 @@
 import java.io.InterruptedIOException;
 import java.nio.channels.ClosedByInterruptException;
 import java.util.concurrent.Callable;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
 /**
@@ -67,7 +68,7 @@ public static final class RetryInterruptedException extends RetryHelperException
     RetryInterruptedException() {}
 
     /**
-     * Sets the caller thread interrupt flag and throws {@code RetryInteruptedException}.
+     * Sets the caller thread interrupt flag and throws {@code RetryInterruptedException}.
      */
     public static void propagate() throws RetryInterruptedException {
       Thread.currentThread().interrupt();
@@ -147,9 +148,11 @@ static Context getContext() {
   @Override
   public String toString() {
     ToStringHelper toStringHelper = MoreObjects.toStringHelper(this);
+    toStringHelper.add("params", params);
     toStringHelper.add("stopwatch", stopwatch);
-    toStringHelper.add("attempNumber", attemptNumber);
+    toStringHelper.add("attemptNumber", attemptNumber);
     toStringHelper.add("callable", callable);
+    toStringHelper.add("exceptionHandler", exceptionHandler);
     return toStringHelper.toString();
   }
 
@@ -160,7 +163,7 @@ private V doRetry() throws RetryHelperException {
       Exception exception;
       try {
         V value = callable.call();
-        if (attemptNumber > 1) {
+        if (attemptNumber > 1 && log.isLoggable(Level.FINE)) {
           log.fine(this + ": attempt #" + attemptNumber + " succeeded");
         }
         return value;
@@ -181,11 +184,14 @@ private V doRetry() throws RetryHelperException {
         throw new RetriesExhaustedException(this + ": Too many failures, giving up", exception);
       }
       long sleepDurationMillis = getSleepDuration(params, attemptNumber);
-      log.fine(this + ": Attempt #" + attemptNumber + " failed [" + exception + "], sleeping for "
-          + sleepDurationMillis + " ms");
+      if (log.isLoggable(Level.FINE)) {
+        log.fine(this + ": Attempt #" + attemptNumber + " failed [" + exception + "], sleeping for "
+            + sleepDurationMillis + " ms");
+      }
       try {
         Thread.sleep(sleepDurationMillis);
       } catch (InterruptedException e) {
+        // propagate as RetryInterruptedException
         RetryInterruptedException.propagate();
       }
     }
diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java
index c23d754a5c46..b34875f5f87f 100644
--- a/src/main/java/com/google/gcloud/ServiceOptions.java
+++ b/src/main/java/com/google/gcloud/ServiceOptions.java
@@ -2,6 +2,7 @@
 
 
 import static com.google.common.base.MoreObjects.firstNonNull;
+import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.api.client.extensions.appengine.http.UrlFetchTransport;
 import com.google.api.client.http.HttpRequestInitializer;
@@ -11,6 +12,7 @@
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.lang.reflect.Method;
 import java.net.URL;
 import java.net.URLConnection;
 import java.util.Set;
@@ -121,10 +123,29 @@ protected static String googleCloudProjectId() {
       URLConnection connection = url.openConnection();
       connection.setRequestProperty("X-Google-Metadata-Request", "True");
       try (BufferedReader reader =
-          new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
+               new BufferedReader(new InputStreamReader(connection.getInputStream(), UTF_8))) {
         return reader.readLine();
       }
-    } catch (IOException e) {
+    } catch (IOException ignore) {
+      // return null if can't determine
+      return null;
+    }
+  }
+
+  protected static String getAppEngineProjectId() {
+    // TODO(ozarov): An alternative to reflection would be to depend on AE api jar:
+    // http://mvnrepository.com/artifact/com.google.appengine/appengine-api-1.0-sdk/1.2.0
+    try {
+      Class factoryClass =
+          Class.forName("com.google.appengine.api.appidentity.AppIdentityServiceFactory");
+      Method method = factoryClass.getMethod("getAppIdentityService");
+      Object appIdentityService = method.invoke(null);
+      method = appIdentityService.getClass().getMethod("getServiceAccountName");
+      String serviceAccountName = (String) method.invoke(appIdentityService);
+      int indexOfAtSign = serviceAccountName.indexOf('@');
+      return serviceAccountName.substring(0, indexOfAtSign);
+    } catch (Exception ignore) {
+      // return null if can't determine
       return null;
     }
   }
diff --git a/src/main/java/com/google/gcloud/datastore/BaseEntity.java b/src/main/java/com/google/gcloud/datastore/BaseEntity.java
index 91665fd59987..a760d98fa90e 100644
--- a/src/main/java/com/google/gcloud/datastore/BaseEntity.java
+++ b/src/main/java/com/google/gcloud/datastore/BaseEntity.java
@@ -28,20 +28,20 @@ abstract class BaseEntity extends Serializable {
 
   private final transient ImmutableSortedMap> properties;
 
-  protected abstract static class Builder> {
+  abstract static class Builder> {
 
-    protected final Map> properties;
+    final Map> properties;
 
-    protected Builder() {
+    Builder() {
       properties = new HashMap<>();
     }
 
-    protected Builder(BaseEntity entity) {
+    Builder(BaseEntity entity) {
       properties = new HashMap<>(entity.properties());
     }
 
     @SuppressWarnings("unchecked")
-    protected B self() {
+    B self() {
       return (B) this;
     }
 
@@ -124,7 +124,7 @@ public B setNull(String name) {
     public abstract BaseEntity build();
   }
 
-  protected BaseEntity(ImmutableSortedMap> properties) {
+  BaseEntity(ImmutableSortedMap> properties) {
     this.properties = properties;
   }
 
@@ -153,41 +153,49 @@ public boolean isNull(String name) {
     return getValue(name) instanceof NullValue;
   }
 
+  @SuppressWarnings("unchecked")
   public String getString(String name) {
-    return ((StringValue) getValue(name)).get();
+    return ((Value) getValue(name)).get();
   }
 
+  @SuppressWarnings("unchecked")
   public long getLong(String name) {
-    return ((LongValue) getValue(name)).get();
+    return ((Value) getValue(name)).get();
   }
 
+  @SuppressWarnings("unchecked")
   public double getDouble(String name) {
-    return ((DoubleValue) getValue(name)).get();
+    return ((Value) getValue(name)).get();
   }
 
+  @SuppressWarnings("unchecked")
   public boolean getBoolean(String name) {
-    return ((BooleanValue) getValue(name)).get();
+    return ((Value) getValue(name)).get();
   }
 
+  @SuppressWarnings("unchecked")
   public DateTime getDateTime(String name) {
-    return ((DateTimeValue) getValue(name)).get();
+    return ((Value) getValue(name)).get();
   }
 
+  @SuppressWarnings("unchecked")
   public Key getKey(String name) {
-    return ((KeyValue) getValue(name)).get();
+    return ((Value) getValue(name)).get();
   }
 
   @SuppressWarnings("unchecked")
   public  T getEntity(String name) {
-    return (T) ((EntityValue) getValue(name)).get();
+    return (T) ((Value) getValue(name)).get();
   }
 
+  @SuppressWarnings("unchecked")
   public List> getList(String name) {
-    return ((ListValue) getValue(name)).get();
+    return ((Value>>) getValue(name)).get();
   }
 
+  @SuppressWarnings("unchecked")
   public Blob getBlob(String name) {
-    return ((BlobValue) getValue(name)).get();
+    return ((Value) getValue(name)).get();
   }
 
   /**
diff --git a/src/main/java/com/google/gcloud/datastore/BaseKey.java b/src/main/java/com/google/gcloud/datastore/BaseKey.java
index ea9f600aff7e..534f390ca67c 100644
--- a/src/main/java/com/google/gcloud/datastore/BaseKey.java
+++ b/src/main/java/com/google/gcloud/datastore/BaseKey.java
@@ -25,24 +25,24 @@ abstract class BaseKey extends Serializable {
 
   abstract static class Builder> {
 
-    protected String dataset;
-    protected String namespace;
-    protected String kind;
-    protected final List ancestors;
+    String dataset;
+    String namespace;
+    String kind;
+    final List ancestors;
 
     private static final int MAX_PATH = 100;
 
-    public Builder(String dataset) {
+    Builder(String dataset) {
       this.dataset = validateDataset(dataset);
       ancestors = new LinkedList<>();
     }
 
-    public Builder(String dataset, String kind) {
+    Builder(String dataset, String kind) {
       this(dataset);
       this.kind = validateKind(kind);
     }
 
-    public Builder(BaseKey copyFrom) {
+    Builder(BaseKey copyFrom) {
       dataset = copyFrom.dataset();
       namespace = copyFrom.namespace();
       ancestors = new LinkedList<>(copyFrom.ancestors());
@@ -50,7 +50,7 @@ public Builder(BaseKey copyFrom) {
     }
 
     @SuppressWarnings("unchecked")
-    protected B self() {
+    B self() {
       return (B) this;
     }
 
@@ -120,11 +120,11 @@ public List ancestors() {
   /**
    * Returns an immutable list of the key's path (ancestors + self).
    */
-  public List path() {
+  List path() {
     return path;
   }
 
-  protected PathElement leaf() {
+  PathElement leaf() {
     return path().get(path().size() - 1);
   }
 
diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
index 83cc6cb0808c..273d46f254a3 100644
--- a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
@@ -17,7 +17,7 @@ class BatchWriterImpl implements BatchWriter {
   private final Map toPut = new LinkedHashMap<>();
   private final Set toDelete = new LinkedHashSet<>();
   private final boolean force;
-  protected final DatastoreServiceImpl datastore;
+  final DatastoreServiceImpl datastore;
 
   private boolean active = true;
 
@@ -32,19 +32,19 @@ class BatchWriterImpl implements BatchWriter {
     }
   }
 
-  protected void checkActive() {
+  void validateActive() {
     if (!active) {
       throw throwInvalidRequest(getName() + " is no longer active");
     }
   }
 
-  protected String getName() {
+  String getName() {
     return "batch";
   }
 
   @Override
   public void add(Entity... entities) {
-    checkActive();
+    validateActive();
     for (Entity entity : entities) {
       Key key = entity.key();
       if (toAdd.containsKey(key) || toUpdate.containsKey(key) || toPut.containsKey(key)) {
@@ -61,7 +61,7 @@ public void add(Entity... entities) {
 
   @Override
   public void update(Entity... entities) {
-    checkActive();
+    validateActive();
     for (Entity entity : entities) {
       Key key = entity.key();
       if (toDelete.contains(key)) {
@@ -78,7 +78,7 @@ public void update(Entity... entities) {
 
   @Override
   public void put(Entity... entities) {
-    checkActive();
+    validateActive();
     for (Entity entity : entities) {
       Key key = entity.key();
       toAdd.remove(key);
@@ -90,7 +90,7 @@ public void put(Entity... entities) {
 
   @Override
   public void delete(Key... keys) {
-    checkActive();
+    validateActive();
     for (Key key : keys) {
       toAdd.remove(key);
       toUpdate.remove(key);
@@ -101,7 +101,7 @@ public void delete(Key... keys) {
 
   @Override
   public void submit() {
-    checkActive();
+    validateActive();
     DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
     for (Entity entity : toAdd.values()) {
       mutationPb.addInsert(entity.toPb());
@@ -129,7 +129,7 @@ public boolean active() {
     return active;
   }
 
-  protected DatastoreV1.CommitRequest.Builder newCommitRequest() {
+  DatastoreV1.CommitRequest.Builder newCommitRequest() {
     DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder();
     requestPb.setMode(DatastoreV1.CommitRequest.Mode.NON_TRANSACTIONAL);
     return requestPb;
diff --git a/src/main/java/com/google/gcloud/datastore/Blob.java b/src/main/java/com/google/gcloud/datastore/Blob.java
index b78dcc6ae922..b8d6892d201f 100644
--- a/src/main/java/com/google/gcloud/datastore/Blob.java
+++ b/src/main/java/com/google/gcloud/datastore/Blob.java
@@ -57,10 +57,7 @@ public int hashCode() {
 
   @Override
   public boolean equals(Object obj) {
-    if (obj == this) {
-      return true;
-    }
-    return obj instanceof Blob && byteString.equals(((Blob) obj).byteString);
+    return obj == this || obj instanceof Blob && byteString.equals(((Blob) obj).byteString);
   }
 
   /**
@@ -90,7 +87,7 @@ public ByteBuffer asReadOnlyByteBuffer() {
   public InputStream asInputStream() {
     final ByteBuffer byteBuffer = asReadOnlyByteBuffer();
     return new InputStream() {
-      @Override public int read() throws IOException {
+      @Override public int read() {
         return !byteBuffer.hasRemaining() ? -1 : byteBuffer.get() & 0xFF;
       }
     };
diff --git a/src/main/java/com/google/gcloud/datastore/Cursor.java b/src/main/java/com/google/gcloud/datastore/Cursor.java
index 6f4a0b682ab0..25b35f6bcfe6 100644
--- a/src/main/java/com/google/gcloud/datastore/Cursor.java
+++ b/src/main/java/com/google/gcloud/datastore/Cursor.java
@@ -35,10 +35,7 @@ public int hashCode() {
 
   @Override
   public boolean equals(Object obj) {
-    if (obj == this) {
-      return true;
-    }
-    return obj instanceof Cursor && byteString.equals(((Cursor) obj).byteString);
+    return obj == this || obj instanceof Cursor && byteString.equals(((Cursor) obj).byteString);
   }
 
   @Override
@@ -72,7 +69,7 @@ public String toUrlSafe() {
   public static Cursor fromUrlSafe(String urlSafe) {
     try {
       String utf8Str = URLDecoder.decode(urlSafe, UTF_8.name());
-      return fromPb(DatastoreV1.Value.parseFrom(utf8Str.getBytes()));
+      return fromPb(DatastoreV1.Value.parseFrom(ByteString.copyFromUtf8(utf8Str)));
     } catch (UnsupportedEncodingException | InvalidProtocolBufferException e) {
       throw new IllegalStateException("Unexpected decoding exception", e);
     }
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
index a887c03076df..024975b8cd03 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
@@ -15,6 +15,8 @@
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.Callable;
 
 
@@ -28,12 +30,12 @@ final class DatastoreServiceImpl implements DatastoreService {
         private static final long serialVersionUID = 6911242958397733203L;
 
         @Override
-        public RetryResult shouldRetry(Exception exception, RetryResult retryResult) {
+        public RetryResult afterEval(Exception exception, RetryResult retryResult) {
           return null;
         }
 
         @Override
-        public RetryResult shouldRetry(Exception exception) {
+        public RetryResult beforeEval(Exception exception) {
           if (exception instanceof DatastoreServiceException) {
             boolean isTransient = ((DatastoreServiceException) exception).code().isTransient();
             return isTransient
@@ -63,13 +65,13 @@ public DatastoreServiceOptions options() {
   }
 
   @Override
-  public BatchWriter newBatchWriter(BatchWriteOption... batchWriteOption) {
-    return new BatchWriterImpl(this, batchWriteOption);
+  public BatchWriter newBatchWriter(BatchWriteOption... options) {
+    return new BatchWriterImpl(this, options);
   }
 
   @Override
-  public Transaction newTransaction(TransactionOption... transactionOption) {
-    return new TransactionImpl(this, transactionOption);
+  public Transaction newTransaction(TransactionOption... options) {
+    return new TransactionImpl(this, options);
   }
 
   @Override
@@ -209,9 +211,9 @@ DatastoreV1.LookupResponse lookup(final DatastoreV1.LookupRequest requestPb) {
   }
 
   @Override
-  public void add(Entity... entities) {
+    public void add(Entity... entities) {
     DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
-    LinkedHashSet keys = new LinkedHashSet<>();
+    Set keys = new LinkedHashSet<>();
     for (Entity entity : entities) {
       if (!keys.add(entity.key())) {
         throw DatastoreServiceException.throwInvalidRequest(
@@ -225,7 +227,7 @@ public void add(Entity... entities) {
   @Override
   public void update(Entity... entities) {
     DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
-    LinkedHashMap dedupEntities = new LinkedHashMap<>();
+    Map dedupEntities = new LinkedHashMap<>();
     for (Entity entity : entities) {
       dedupEntities.put(entity.key(), entity);
     }
@@ -238,7 +240,7 @@ public void update(Entity... entities) {
   @Override
   public void put(Entity... entities) {
     DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
-    LinkedHashMap dedupEntities = new LinkedHashMap<>();
+    Map dedupEntities = new LinkedHashMap<>();
     for (Entity entity : entities) {
       dedupEntities.put(entity.key(), entity);
     }
@@ -251,7 +253,7 @@ public void put(Entity... entities) {
   @Override
   public void delete(Key... keys) {
     DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
-    LinkedHashSet dedupKeys = new LinkedHashSet<>(Arrays.asList(keys));
+    Set dedupKeys = new LinkedHashSet<>(Arrays.asList(keys));
     for (Key key : dedupKeys) {
       mutationPb.addDelete(key.toPb());
     }
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java
index 009b663ab874..262d8f81060f 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java
@@ -73,7 +73,7 @@ public Builder force(boolean force) {
     }
   }
 
-  DatastoreServiceOptions(Builder builder) {
+  private DatastoreServiceOptions(Builder builder) {
     super(builder);
     namespace = builder.namespace != null ? builder.namespace : defaultNamespace();
     force = builder.force;
@@ -140,8 +140,9 @@ private static String defaultNamespace() {
       Class clazz = Class.forName("com.google.appengine.api.NamespaceManager");
       Method method = clazz.getMethod("get");
       String namespace = (String) method.invoke(null);
-      return "".equals(namespace) ? null : namespace;
-    } catch (Exception ex) {
+      return namespace == null || namespace.isEmpty() ? null : namespace;
+    } catch (Exception ignore) {
+      // return null (Datastore default namespace) if could not automatically determine
       return null;
     }
   }
diff --git a/src/main/java/com/google/gcloud/datastore/DateTime.java b/src/main/java/com/google/gcloud/datastore/DateTime.java
index 8c9ec1dbdca6..a2e8c6398351 100644
--- a/src/main/java/com/google/gcloud/datastore/DateTime.java
+++ b/src/main/java/com/google/gcloud/datastore/DateTime.java
@@ -39,10 +39,8 @@ public int hashCode() {
 
   @Override
   public boolean equals(Object obj) {
-    if (obj == this) {
-      return true;
-    }
-    return obj instanceof DateTime && timestampMicroseconds == ((DateTime) obj).timestampMicroseconds;
+    return obj == this || obj instanceof DateTime
+        && timestampMicroseconds == ((DateTime) obj).timestampMicroseconds;
   }
 
   public long timestampMicroseconds() {
diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
index 72d3f8c8e5bc..9c5c982b82c4 100644
--- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java
@@ -378,15 +378,14 @@ protected GqlQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) {
   }
 
   @Override
-  protected Object fromPb(Type resultType, String namespace, byte[] bytesPb)
+  protected Object fromPb(Type type, String namespace, byte[] bytesPb)
       throws InvalidProtocolBufferException {
-    return fromPb(resultType, namespace, DatastoreV1.GqlQuery.parseFrom(bytesPb));
+    return fromPb(type, namespace, DatastoreV1.GqlQuery.parseFrom(bytesPb));
   }
 
-  static  GqlQuery fromPb(Type resultType, String namespace,
-      DatastoreV1.GqlQuery queryPb) {
-    Builder builder = new Builder<>(resultType, queryPb.getQueryString());
-    builder.namespace(namespace);
+  private static  GqlQuery fromPb(Type type, String ns, DatastoreV1.GqlQuery queryPb) {
+    Builder builder = new Builder<>(type, queryPb.getQueryString());
+    builder.namespace(ns);
     if (queryPb.hasAllowLiteral()) {
       builder.allowLiteral = queryPb.getAllowLiteral();
     }
diff --git a/src/main/java/com/google/gcloud/datastore/PathElement.java b/src/main/java/com/google/gcloud/datastore/PathElement.java
index 9e84764e55f7..b3ec8b55e5d3 100644
--- a/src/main/java/com/google/gcloud/datastore/PathElement.java
+++ b/src/main/java/com/google/gcloud/datastore/PathElement.java
@@ -89,11 +89,12 @@ protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException {
   static PathElement fromPb(DatastoreV1.Key.PathElement pathElementPb) {
     String kind = pathElementPb.getKind();
     if (pathElementPb.hasId()) {
-      return PathElement.of(kind, pathElementPb.getId());
-    } else if (pathElementPb.hasName()) {
-      return PathElement.of(kind, pathElementPb.getName());
+      return of(kind, pathElementPb.getId());
     }
-    return PathElement.of(kind);
+    if (pathElementPb.hasName()) {
+      return of(kind, pathElementPb.getName());
+    }
+    return of(kind);
   }
 
   static PathElement of(String kind) {
diff --git a/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java b/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java
index 6b219ce0968c..22201f82dda4 100644
--- a/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java
+++ b/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java
@@ -78,22 +78,24 @@ public Key key() {
     return key;
   }
 
+  @SuppressWarnings("unchecked")
   @Override
   public DateTime getDateTime(String name) {
     Value value = getValue(name);
     if (value.hasMeaning() && value.meaning() == 18 && value instanceof LongValue) {
       return new DateTime(getLong(name));
     }
-    return ((DateTimeValue) value).get();
+    return ((Value) value).get();
   }
 
+  @SuppressWarnings("unchecked")
   @Override
   public Blob getBlob(String name) {
     Value value = getValue(name);
     if (value.hasMeaning() && value.meaning() == 18 && value instanceof StringValue) {
       return new Blob(ByteString.copyFromUtf8(getString(name)), false);
     }
-    return ((BlobValue) value).get();
+    return ((Value) value).get();
   }
 
   @Override
diff --git a/src/main/java/com/google/gcloud/datastore/Query.java b/src/main/java/com/google/gcloud/datastore/Query.java
index c68e3246f62f..17084249c646 100644
--- a/src/main/java/com/google/gcloud/datastore/Query.java
+++ b/src/main/java/com/google/gcloud/datastore/Query.java
@@ -9,7 +9,7 @@
 import com.google.protobuf.GeneratedMessage;
 import com.google.protobuf.InvalidProtocolBufferException;
 
-import java.util.EnumMap;
+import java.util.Map;
 
 
 /**
@@ -35,7 +35,7 @@ public abstract class Query extends Serializable {
   public abstract static class Type implements java.io.Serializable {
 
     private static final long serialVersionUID = 2104157695425806623L;
-    private static final EnumMap>
+    private static final Map>
         PB_TO_INSTANCE = Maps.newEnumMap(DatastoreV1.EntityResult.ResultType.class);
 
     static final Type UNKNOWN = new Type(null, Object.class) {
@@ -127,7 +127,7 @@ boolean isAssignableFrom(Type otherType) {
       return resultClass.isAssignableFrom(otherType.resultClass);
     }
 
-    protected abstract V convert(DatastoreV1.Entity value);
+    protected abstract V convert(DatastoreV1.Entity entityPb);
 
     static Type fromPb(DatastoreV1.EntityResult.ResultType typePb) {
       return MoreObjects.firstNonNull(PB_TO_INSTANCE.get(typePb), UNKNOWN);
@@ -150,6 +150,7 @@ public String namespace() {
   @Override
   public String toString() {
     ToStringHelper toStringHelper = MoreObjects.toStringHelper(this);
+    toStringHelper.add("type", type);
     toStringHelper.add("namespace", namespace);
     toStringHelper.add("queryPb", super.toString());
     return toStringHelper.toString();
diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
index 5c32f028b6b3..f1f6ee2f4838 100644
--- a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java
@@ -7,6 +7,7 @@
 import com.google.gcloud.datastore.Query.Type;
 
 import java.util.Iterator;
+import java.util.Objects;
 
 class QueryResultImpl extends AbstractIterator implements QueryResult {
 
@@ -51,7 +52,7 @@ private void sendRequest() {
     entityResultPbIter = queryResultBatchPb.getEntityResultList().iterator();
     // cursor = resultPb.getSkippedCursor(); // only available in v1beta3
     actualType = Type.fromPb(queryResultBatchPb.getEntityResultType());
-    if (queryType == Type.PROJECTION) {
+    if (Objects.equals(queryType, Type.PROJECTION)) {
       // projection entity can represent all type of results
       actualType = Type.PROJECTION;
     }
diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index 7cd197e27ee4..c02e7e5fc91a 100644
--- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -17,7 +17,11 @@
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import java.io.Serializable;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
 
 /**
  * An implementation of a Google Cloud Datastore Query that can be constructed by providing
@@ -137,7 +141,7 @@ public boolean equals(Object obj) {
         return false;
       }
       CompositeFilter other = (CompositeFilter) obj;
-      return operator.equals(other.operator)
+      return operator == other.operator
           && filters.equals(other.filters);
     }
 
@@ -228,7 +232,7 @@ public boolean equals(Object obj) {
       }
       PropertyFilter other = (PropertyFilter) obj;
       return property.equals(other.property)
-          && operator.equals(other.operator)
+          && operator == other.operator
           && Objects.equals(value, other.value);
     }
 
@@ -453,7 +457,7 @@ public boolean equals(Object obj) {
       }
       OrderBy other = (OrderBy) obj;
       return property.equals(other.property)
-          && direction.equals(other.direction);
+          && direction == other.direction;
     }
 
     public String property() {
@@ -591,7 +595,7 @@ static class BaseBuilder> {
     }
 
     @SuppressWarnings("unchecked")
-    protected B self() {
+    B self() {
       return (B) this;
     }
 
@@ -649,43 +653,41 @@ public B addOrderBy(OrderBy orderBy, OrderBy... others) {
       return self();
     }
 
-    protected B clearProjection() {
+    B clearProjection() {
       projection.clear();
       return self();
     }
 
-    protected B projection(Projection projection, Projection... others) {
+    B projection(Projection projection, Projection... others) {
       clearProjection();
       addProjection(projection, others);
       return self();
     }
 
-    protected B addProjection(Projection projection, Projection... others) {
+    B addProjection(Projection projection, Projection... others) {
       this.projection.add(projection);
-      for (Projection other : others) {
-        this.projection.add(other);
-      }
+      Collections.addAll(this.projection, others);
       return self();
     }
 
-    protected B clearGroupBy() {
+    B clearGroupBy() {
       groupBy.clear();
       return self();
     }
 
-    protected B groupBy(String property, String... others) {
+    B groupBy(String property, String... others) {
       clearGroupBy();
       addGroupBy(property, others);
       return self();
     }
 
-    protected B addGroupBy(String property, String... others) {
+    B addGroupBy(String property, String... others) {
       this.groupBy.add(property);
       Collections.addAll(this.groupBy, others);
       return self();
     }
 
-    protected B mergeFrom(DatastoreV1.Query queryPb) {
+    B mergeFrom(DatastoreV1.Query queryPb) {
       if (queryPb.getKindCount() > 0) {
         kind(queryPb.getKind(0).getName());
       }
@@ -869,7 +871,7 @@ protected boolean keyOnly() {
     return projection.size() == 1 && projection.get(0).property.equals(KEY_PROPERTY_NAME);
   }
 
-  protected List projection() {
+  List projection() {
     return projection;
   }
 
@@ -877,7 +879,7 @@ public Filter filter() {
     return filter;
   }
 
-  protected List groupBy() {
+  List groupBy() {
     return groupBy;
   }
 
@@ -961,7 +963,7 @@ protected Object fromPb(Type type, String namespace, byte[] bytesPb)
     return fromPb(type, namespace, DatastoreV1.Query.parseFrom(bytesPb));
   }
 
-  static StructuredQuery fromPb(Type type, String namespace, DatastoreV1.Query queryPb) {
+  private static StructuredQuery fromPb(Type type, String namespace, DatastoreV1.Query queryPb) {
     BaseBuilder builder;
     if (type.equals(Type.FULL)) {
       builder = builder();
diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java
index 0bd51b0e8615..44b231b81383 100644
--- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java
@@ -48,7 +48,7 @@ public Entity get(Key key) {
 
   @Override
   public Iterator get(Key key, Key... others) {
-    checkActive();
+    validateActive();
     DatastoreV1.ReadOptions.Builder readOptionsPb = DatastoreV1.ReadOptions.newBuilder();
     readOptionsPb.setTransaction(transaction);
     return datastore.get(readOptionsPb.build(), key, others);
@@ -56,7 +56,7 @@ public Iterator get(Key key, Key... others) {
 
   @Override
   public  QueryResult run(Query query) {
-    checkActive();
+    validateActive();
     DatastoreV1.ReadOptions.Builder readOptionsPb = DatastoreV1.ReadOptions.newBuilder();
     readOptionsPb.setTransaction(transaction);
     return datastore.run(readOptionsPb.build(), query);
@@ -69,7 +69,7 @@ public void commit() {
 
   @Override
   public void rollback() {
-    super.checkActive();
+    super.validateActive();
     if (!wasRolledback) {
       datastore.rollbackTransaction(transaction);
     }
@@ -87,8 +87,8 @@ protected String getName() {
   }
 
   @Override
-  protected void checkActive() {
-    super.checkActive();
+  protected void validateActive() {
+    super.validateActive();
     if (wasRolledback) {
       throw throwInvalidRequest(getName() + " is not active (was rolledback)");
     }
diff --git a/src/main/java/com/google/gcloud/datastore/Validator.java b/src/main/java/com/google/gcloud/datastore/Validator.java
index 4d4afbc120b4..d2586573d9e8 100644
--- a/src/main/java/com/google/gcloud/datastore/Validator.java
+++ b/src/main/java/com/google/gcloud/datastore/Validator.java
@@ -23,8 +23,8 @@ private Validator() {
 
   static String validateDataset(String dataset) {
     checkArgument(!Strings.isNullOrEmpty(dataset), "dataset can't be empty or null");
-    checkArgument(Validator.DATASET_PATTERN.matcher(dataset).matches(),
-          "dataset must match the following pattern: " + Validator.DATASET_PATTERN.pattern());
+    checkArgument(DATASET_PATTERN.matcher(dataset).matches(),
+        "dataset must match the following pattern: " + DATASET_PATTERN.pattern());
     return dataset;
   }
 
@@ -33,8 +33,8 @@ static String validateNamespace(String namespace) {
       checkArgument(!namespace.isEmpty(), "namespace must not be an empty string");
       checkArgument(namespace.length() <= 100,
           "namespace must not contain more than 100 characters");
-      checkArgument(Validator.NAMESPACE_PATTERN.matcher(namespace).matches(),
-          "namespace must the following pattern: " + Validator.NAMESPACE_PATTERN.pattern());
+      checkArgument(NAMESPACE_PATTERN.matcher(namespace).matches(),
+          "namespace must the following pattern: " + NAMESPACE_PATTERN.pattern());
     }
     return namespace;
   }
diff --git a/src/main/java/com/google/gcloud/datastore/Value.java b/src/main/java/com/google/gcloud/datastore/Value.java
index 4686624adf54..c45e8bdaa0fd 100644
--- a/src/main/java/com/google/gcloud/datastore/Value.java
+++ b/src/main/java/com/google/gcloud/datastore/Value.java
@@ -183,7 +183,7 @@ abstract static class BaseBuilder, B extends BaseBuilder factoryClass =
-          Class.forName("com.google.appengine.api.appidentity.AppIdentityServiceFactory");
-      Method method = factoryClass.getMethod("getAppIdentityService");
-      Object appIdentityService = method.invoke(null);
-      method = appIdentityService.getClass().getMethod("getServiceAccountName");
-      String serviceAccountName = (String) method.invoke(appIdentityService);
-      int indexOfAtSign = serviceAccountName.indexOf('@');
-      return serviceAccountName.substring(0, indexOfAtSign);
-    } catch (Exception ex) {
-      return null;
-    }
-  }
-
   public static class Builder extends ServiceOptions.Builder {
 
     private String project;
diff --git a/src/test/java/com/google/gcloud/ExceptionHandlerTest.java b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java
index 35f9613c42ae..57706ece50d1 100644
--- a/src/test/java/com/google/gcloud/ExceptionHandlerTest.java
+++ b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java
@@ -35,28 +35,28 @@ class B extends A {
     class C extends A {
       @Override
       public Object call() throws FileNotFoundException {
-        return null;
+        return "c";
       }
     }
 
     class D extends C {
       @Override
       public Object call() throws IllegalArgumentException {
-        return null;
+        return "d";
       }
     }
 
     class E extends A {
       @Override
       public String call() throws NullPointerException {
-        return null;
+        return "e";
       }
     }
 
     class F extends A {
       @Override
       public Object call() throws Error {
-        return null;
+        return "f";
       }
     }
 
@@ -93,7 +93,6 @@ private static  void assertInvalidCallable(Callable callable, ExceptionHan
     }
   }
 
-  @SuppressWarnings("serial")
   @Test
   public void testShouldTry() {
     ExceptionHandler handler = ExceptionHandler.builder().retryOn(IOException.class).build();
@@ -115,13 +114,16 @@ public void testShouldTry() {
 
     final AtomicReference before = new AtomicReference<>(RetryResult.ABORT);
     Interceptor interceptor = new Interceptor() {
+
+      private static final long serialVersionUID = 1;
+
       @Override
-      public RetryResult shouldRetry(Exception exception, RetryResult retryResult) {
+      public RetryResult afterEval(Exception exception, RetryResult retryResult) {
         return retryResult == RetryResult.ABORT ? RetryResult.RETRY : RetryResult.ABORT;
       }
 
       @Override
-      public RetryResult shouldRetry(Exception exception) {
+      public RetryResult beforeEval(Exception exception) {
         return before.get();
       }
     };
diff --git a/src/test/java/com/google/gcloud/RetryHelperTest.java b/src/test/java/com/google/gcloud/RetryHelperTest.java
index 1fafae35fd50..d22ba9bcc43b 100644
--- a/src/test/java/com/google/gcloud/RetryHelperTest.java
+++ b/src/test/java/com/google/gcloud/RetryHelperTest.java
@@ -68,31 +68,35 @@ public void testTriesWithExceptionHandling() {
     }
     assertNull(RetryHelper.getContext());
 
-    @SuppressWarnings("serial")
-    class E1 extends Exception {}
+    class E1Exception extends Exception {
+      private static final long serialVersionUID = 3874933713392137001L;
+    }
 
-    @SuppressWarnings("serial")
-    class E2 extends E1 {}
+    class E2Exception extends E1Exception {
+      private static final long serialVersionUID = -8710227162480133598L;
+    }
 
-    @SuppressWarnings("serial")
-    class E3 extends E1 {}
+    class E3Exception extends E1Exception {
+      private static final long serialVersionUID = -7794256022024001666L;
+    }
 
-    @SuppressWarnings("serial")
-    class E4 extends E2 {}
+    class E4Exception extends E2Exception {
+      private static final long serialVersionUID = -5508018234693709156L;
+    }
 
     params = RetryParams.builder().initialRetryDelayMillis(0).retryMaxAttempts(5).build();
-    handler = ExceptionHandler.builder().retryOn(E1.class, E4.class).abortOn(E3.class).build();
-    final Iterator exceptions =
-        Arrays.asList(new E1(), new E2(), new E4(), new E3()).iterator();
+    handler = ExceptionHandler.builder().retryOn(E1Exception.class, E4Exception.class).abortOn(E3Exception.class).build();
+    final Iterator exceptions =
+        Arrays.asList(new E1Exception(), new E2Exception(), new E4Exception(), new E3Exception()).iterator();
     try {
       RetryHelper.runWithRetries(new Callable() {
-        @Override public Void call() throws E1 {
+        @Override public Void call() throws E1Exception {
           throw exceptions.next();
         }
       }, params, handler);
       fail("Exception should have been thrown");
     } catch (NonRetriableException ex) {
-      assertTrue(ex.getCause() instanceof E3);
+      assertTrue(ex.getCause() instanceof E3Exception);
     }
     assertNull(RetryHelper.getContext());
   }
@@ -108,7 +112,7 @@ public void testTriesAtLeastMinTimes() {
     final int timesToFail = 7;
     assertNull(RetryHelper.getContext());
     int attempted = RetryHelper.runWithRetries(new Callable() {
-      int timesCalled = 0;
+      int timesCalled;
       @Override public Integer call() throws IOException {
         timesCalled++;
         assertEquals(timesCalled, RetryHelper.getContext().getAttemptNumber());
@@ -151,7 +155,7 @@ public void testTriesNoMoreThanMaxTimes() {
     }
   }
 
-  private class FakeTicker extends Ticker {
+  private static class FakeTicker extends Ticker {
     private final AtomicLong nanos = new AtomicLong();
 
     // Advances the ticker value by {@code time} in {@code timeUnit}.
@@ -196,7 +200,8 @@ public void testTriesNoMoreLongerThanTotalRetryPeriod() {
         }
       }), params, handler, stopwatch);
       fail();
-    } catch (RetriesExhaustedException e) {
+    } catch (RetriesExhaustedException expected) {
+      // verify timesCalled
       assertEquals(sleepOnAttempt, timesCalled.get());
     }
   }
@@ -213,29 +218,29 @@ public void testBackoffIsExponential() {
         .retryMaxAttempts(100)
         .build();
     long sleepDuration = RetryHelper.getSleepDuration(params, 1);
-    assertTrue("" + sleepDuration, sleepDuration < 13 && sleepDuration >= 7);
+    assertTrue(String.valueOf(sleepDuration), sleepDuration < 13 && sleepDuration >= 7);
     sleepDuration = RetryHelper.getSleepDuration(params, 2);
-    assertTrue("" + sleepDuration, sleepDuration < 25 && sleepDuration >= 15);
+    assertTrue(String.valueOf(sleepDuration), sleepDuration < 25 && sleepDuration >= 15);
     sleepDuration = RetryHelper.getSleepDuration(params, 3);
-    assertTrue("" + sleepDuration, sleepDuration < 50 && sleepDuration >= 30);
+    assertTrue(String.valueOf(sleepDuration), sleepDuration < 50 && sleepDuration >= 30);
     sleepDuration = RetryHelper.getSleepDuration(params, 4);
-    assertTrue("" + sleepDuration, sleepDuration < 100 && sleepDuration >= 60);
+    assertTrue(String.valueOf(sleepDuration), sleepDuration < 100 && sleepDuration >= 60);
     sleepDuration = RetryHelper.getSleepDuration(params, 5);
-    assertTrue("" + sleepDuration, sleepDuration < 200 && sleepDuration >= 120);
+    assertTrue(String.valueOf(sleepDuration), sleepDuration < 200 && sleepDuration >= 120);
     sleepDuration = RetryHelper.getSleepDuration(params, 6);
-    assertTrue("" + sleepDuration, sleepDuration < 400 && sleepDuration >= 240);
+    assertTrue(String.valueOf(sleepDuration), sleepDuration < 400 && sleepDuration >= 240);
     sleepDuration = RetryHelper.getSleepDuration(params, 7);
-    assertTrue("" + sleepDuration, sleepDuration < 800 && sleepDuration >= 480);
+    assertTrue(String.valueOf(sleepDuration), sleepDuration < 800 && sleepDuration >= 480);
     sleepDuration = RetryHelper.getSleepDuration(params, 8);
-    assertTrue("" + sleepDuration, sleepDuration < 1600 && sleepDuration >= 960);
+    assertTrue(String.valueOf(sleepDuration), sleepDuration < 1600 && sleepDuration >= 960);
     sleepDuration = RetryHelper.getSleepDuration(params, 9);
-    assertTrue("" + sleepDuration, sleepDuration < 3200 && sleepDuration >= 1920);
+    assertTrue(String.valueOf(sleepDuration), sleepDuration < 3200 && sleepDuration >= 1920);
     sleepDuration = RetryHelper.getSleepDuration(params, 10);
-    assertTrue("" + sleepDuration, sleepDuration < 6400 && sleepDuration >= 3840);
+    assertTrue(String.valueOf(sleepDuration), sleepDuration < 6400 && sleepDuration >= 3840);
     sleepDuration = RetryHelper.getSleepDuration(params, 11);
-    assertTrue("" + sleepDuration, sleepDuration < 12800 && sleepDuration >= 7680);
+    assertTrue(String.valueOf(sleepDuration), sleepDuration < 12800 && sleepDuration >= 7680);
     sleepDuration = RetryHelper.getSleepDuration(params, 12);
-    assertTrue("" + sleepDuration, sleepDuration < 25600 && sleepDuration >= 15360);
+    assertTrue(String.valueOf(sleepDuration), sleepDuration < 25600 && sleepDuration >= 15360);
   }
 
   @Test
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
index 1d943abf172d..0dfad2ebf791 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
@@ -65,7 +65,7 @@ public class DatastoreServiceIntegrationTest {
       .set("list", LIST_VALUE2)
       .build();
   private static final Entity ENTITY2 = Entity.builder(ENTITY1).key(KEY2).remove("str")
-      .set("name", "koko").setNull("null").set("age", 20).build();
+      .set("name", "Dan").setNull("null").set("age", 20).build();
   private static final Entity ENTITY3 = Entity.builder(ENTITY1).key(KEY3).remove("str")
       .set("null", NULL_VALUE).set("partial1", PARTIAL_ENTITY2).set("partial2", ENTITY2).build();
 
@@ -372,7 +372,7 @@ public void testRunGqlQueryWithCasting() {
 
     Query query2 = GqlQuery.builder("select * from " + KIND1).build();
     QueryResult results2 = datastore.run(query2);
-    assertEquals(Entity.class, results2.resultClass());
+    assertSame(Entity.class, results2.resultClass());
     @SuppressWarnings("unchecked")
     QueryResult results3 = (QueryResult) results2;
     assertTrue(results3.hasNext());
@@ -437,7 +437,7 @@ public void testRunStructuredQuery() throws DatastoreException {
     ProjectionEntity entity = results4.next();
     assertEquals(ENTITY2.key(), entity.key());
     assertEquals(20, entity.getLong("age"));
-    assertEquals("koko", entity.getString("name"));
+    assertEquals("Dan", entity.getString("name"));
     assertEquals(2, entity.properties().size());
     assertFalse(results4.hasNext());
     EasyMock.verify(mockDatastore);
@@ -522,12 +522,12 @@ public void testGetArray() {
     Entity entity3 = result.next();
     assertEquals(ENTITY3, entity3);
     assertTrue(entity3.isNull("null"));
-    assertEquals(false, entity3.getBoolean("bool"));
+    assertFalse(entity3.getBoolean("bool"));
     assertEquals(LIST_VALUE2.get(), entity3.getList("list"));
     PartialEntity partial1 = entity3.getEntity("partial1");
     Entity partial2 = entity3.getEntity("partial2");
-    assertEquals(partial1, PARTIAL_ENTITY2);
-    assertEquals(partial2, ENTITY2);
+    assertEquals(PARTIAL_ENTITY2, partial1);
+    assertEquals(ENTITY2, partial2);
     assertEquals(Value.Type.BOOLEAN, entity3.getValue("bool").type());
     assertEquals(6, entity3.names().size());
     assertFalse(entity3.contains("bla"));
diff --git a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
index 519b7ae3ee72..4738158eb319 100644
--- a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
+++ b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java
@@ -1,5 +1,7 @@
 package com.google.gcloud.datastore;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import com.google.api.client.util.Strings;
 
 import java.io.BufferedOutputStream;
@@ -35,7 +37,7 @@ public class LocalGcdHelper {
   public static final String DEFAULT_DATASET = "dataset1";
   public static final int PORT = 8080;
   private static final String GCD = "gcd-v1beta2-rev1-2.1.1";
-  private static final String GCD_LOC = "/" + GCD + ".zip";
+  private static final String GCD_LOC = '/' + GCD + ".zip";
 
   private static class ProcessStreamReader extends Thread {
 
@@ -103,7 +105,7 @@ public void start() throws IOException, InterruptedException {
       }
     }
 
-    File datasetFolder = new File(gcdFolder, GCD + "/" + dataset);
+    File datasetFolder = new File(gcdFolder, GCD + '/' + dataset);
     datasetFolder.delete();
 
     // TODO: if System.getProperty("os.name").startsWith("Windows") use cmd.exe /c and gcd.cmd
@@ -193,7 +195,7 @@ public static void main(String... args) throws IOException, InterruptedException
       switch (args[0]) {
         case "START":
           if (!isActive(DEFAULT_DATASET)) {
-            LocalGcdHelper helper = LocalGcdHelper.start(DEFAULT_DATASET);
+            LocalGcdHelper helper = start(DEFAULT_DATASET);
             try (FileWriter writer = new FileWriter(".local_gcd_helper")) {
               writer.write(helper.gcdPath.toAbsolutePath().toString());
             }
@@ -207,8 +209,8 @@ public static void main(String... args) throws IOException, InterruptedException
               String path = reader.readLine();
               deleteRecurse(Paths.get(path));
             }
-          }
           file.delete();
+          }
           return;
         default:
           break;
@@ -217,15 +219,16 @@ public static void main(String... args) throws IOException, InterruptedException
     throw new RuntimeException("expecting only START | STOP");
   }
 
-  @SuppressWarnings("BooleanMethodIsAlwaysInverted")
   public static boolean isActive(String dataset) {
     try {
       String path = "/datastore/v1beta2/datasets/" + dataset + "/lookup";
       URL url = new URL("http://localhost:" + PORT + path);
-      try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
+      try (BufferedReader reader =
+               new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) {
         return "Valid RPC".equals(reader.readLine());
       }
-    } catch (IOException ex) {
+    } catch (IOException ignore) {
+      // assume not active
       return false;
     }
   }
diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
index d87b64d7f480..706485b4759e 100644
--- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java
+++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
@@ -118,7 +118,7 @@ public class SerializationTest {
   public void testValues() throws Exception {
     for (Type type : Type.values()) {
       for (Value value : TYPE_TO_VALUES.get(type)) {
-        Value copy = serialiazeAndDeserialize(value);
+        Value copy = serializeAndDeserialize(value);
         assertEquals(value, value);
         assertEquals(value, copy);
         assertNotSame(value, copy);
@@ -130,11 +130,11 @@ public void testValues() throws Exception {
 
   @Test
   public void testTypes() throws Exception {
-    Object[] types = { KEY1, KEY2, INCOMPLETE_KEY1, INCOMPLETE_KEY2, ENTITY1, ENTITY2,
+    Serializable[] types = { KEY1, KEY2, INCOMPLETE_KEY1, INCOMPLETE_KEY2, ENTITY1, ENTITY2,
         ENTITY3, EMBEDDED_ENTITY1, EMBEDDED_ENTITY2, EMBEDDED_ENTITY3, PROJECTION_ENTITY,
         DATE_TIME1, BLOB1, CURSOR1, GQL1, GQL2, QUERY1, QUERY2, QUERY3};
-    for (Object obj : types) {
-      Object copy = serialiazeAndDeserialize(obj);
+    for (Serializable obj : types) {
+      Object copy = serializeAndDeserialize(obj);
       assertEquals(obj, obj);
       assertEquals(obj, copy);
       assertNotSame(obj, copy);
@@ -143,7 +143,7 @@ public void testTypes() throws Exception {
   }
 
   @SuppressWarnings("unchecked")
-  private  T serialiazeAndDeserialize(T obj) throws IOException, ClassNotFoundException {
+  private  T serializeAndDeserialize(T obj) throws IOException, ClassNotFoundException {
     ByteArrayOutputStream bytes = new ByteArrayOutputStream();
     try (ObjectOutputStream output = new ObjectOutputStream(bytes)) {
       output.writeObject(obj);

From 5325076aabe44284f79ab12bb107f50d40cc74f4 Mon Sep 17 00:00:00 2001
From: aozarov 
Date: Thu, 8 Jan 2015 18:34:58 -0800
Subject: [PATCH 085/771] revert wrong formatting

---
 .../java/com/google/gcloud/RetryHelper.java   |  8 ++---
 .../com/google/gcloud/datastore/BaseKey.java  |  2 +-
 .../datastore/DatastoreServiceException.java  | 32 +++++++++----------
 .../datastore/DatastoreServiceImpl.java       |  4 +--
 .../com/google/gcloud/RetryParamsTest.java    | 14 ++++----
 5 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/src/main/java/com/google/gcloud/RetryHelper.java b/src/main/java/com/google/gcloud/RetryHelper.java
index 3c6e23cf085c..850048a7372d 100644
--- a/src/main/java/com/google/gcloud/RetryHelper.java
+++ b/src/main/java/com/google/gcloud/RetryHelper.java
@@ -1,10 +1,10 @@
 package com.google.gcloud;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static java.lang.Math.max;
-import static java.lang.Math.min;
-import static java.lang.Math.pow;
-import static java.lang.Math.random;
+import static java.lang.StrictMath.max;
+import static java.lang.StrictMath.min;
+import static java.lang.StrictMath.pow;
+import static java.lang.StrictMath.random;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 
 import com.google.common.annotations.VisibleForTesting;
diff --git a/src/main/java/com/google/gcloud/datastore/BaseKey.java b/src/main/java/com/google/gcloud/datastore/BaseKey.java
index 534f390ca67c..b96f817cbe29 100644
--- a/src/main/java/com/google/gcloud/datastore/BaseKey.java
+++ b/src/main/java/com/google/gcloud/datastore/BaseKey.java
@@ -148,7 +148,7 @@ public boolean equals(Object obj) {
     if (!(obj instanceof BaseKey)) {
       return false;
     }
-    PartialKey other = (PartialKey) obj;
+    BaseKey other = (BaseKey) obj;
     return Objects.equals(dataset(), other.dataset())
         && Objects.equals(namespace(), other.namespace())
         && Objects.equals(path(), other.path());
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java
index 863a8cddb6e3..d5f36ebd83ab 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java
@@ -39,13 +39,13 @@ public enum Code {
     INTERNAL(false, "Server returned an error", 500),
     UNKNOWN(false, "Unknown failure", -1);
 
-    private final boolean isTransient;
-    private final String defaultMessage;
+    private final boolean retriable;
+    private final String message;
     private final int httpCode;
 
-    Code(boolean isTransient, String msg, int httpCode) {
-      this.isTransient = isTransient;
-      defaultMessage = msg;
+    Code(boolean retriable, String message, int httpCode) {
+      this.retriable = retriable;
+      this.message = message;
       this.httpCode = httpCode;
     }
 
@@ -57,12 +57,12 @@ public Integer httpCode() {
      * Returns {@code true} if this exception is transient and the same request could be retried.
      * For any retry it is highly recommended to apply an exponential backoff.
      */
-    public boolean isTransient() {
-      return isTransient;
+    public boolean isRetriable() {
+      return retriable;
     }
 
-    DatastoreServiceException translate(DatastoreException exception, String msg) {
-      return new DatastoreServiceException(this, msg, exception);
+    DatastoreServiceException translate(DatastoreException exception, String message) {
+      return new DatastoreServiceException(this, message, exception);
     }
   }
 
@@ -77,13 +77,13 @@ DatastoreServiceException translate(DatastoreException exception, String msg) {
     HTTP_TO_CODE = ImmutableMap.copyOf(httpCodes);
   }
 
-  public DatastoreServiceException(Code code, String msg, Exception cause) {
-    super(MoreObjects.firstNonNull(msg, code.defaultMessage), cause);
+  public DatastoreServiceException(Code code, String message, Exception cause) {
+    super(MoreObjects.firstNonNull(message, code.message), cause);
     this.code = code;
   }
 
-  public DatastoreServiceException(Code code, String msg) {
-    this(code, msg, null);
+  public DatastoreServiceException(Code code, String message) {
+    this(code, message, null);
   }
 
   /**
@@ -131,12 +131,12 @@ static DatastoreServiceException translateAndThrow(DatastoreException exception)
 
 
   /**
-   * Throw a DatastoreServiceException with {@code FAILED_PRECONDITION} code and the {@code msg}
+   * Throw a DatastoreServiceException with {@code FAILED_PRECONDITION} code and the {@code message}
    * in a nested exception.
    *
    * @throws DatastoreServiceException every time
    */
-  static DatastoreServiceException throwInvalidRequest(String msg, Object... params) {
-    throw new DatastoreServiceException(Code.FAILED_PRECONDITION, String.format(msg, params));
+  static DatastoreServiceException throwInvalidRequest(String massage, Object... params) {
+    throw new DatastoreServiceException(Code.FAILED_PRECONDITION, String.format(massage, params));
   }
 }
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
index 024975b8cd03..a68783c95609 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
@@ -37,8 +37,8 @@ public RetryResult afterEval(Exception exception, RetryResult retryResult) {
         @Override
         public RetryResult beforeEval(Exception exception) {
           if (exception instanceof DatastoreServiceException) {
-            boolean isTransient = ((DatastoreServiceException) exception).code().isTransient();
-            return isTransient
+            boolean isRetriable = ((DatastoreServiceException) exception).code().isRetriable();
+            return isRetriable
                 ? ExceptionHandler.Interceptor.RetryResult.RETRY
                 : ExceptionHandler.Interceptor.RetryResult.ABORT;
           }
diff --git a/src/test/java/com/google/gcloud/RetryParamsTest.java b/src/test/java/com/google/gcloud/RetryParamsTest.java
index b59324f0cb1e..744bec7b59a2 100644
--- a/src/test/java/com/google/gcloud/RetryParamsTest.java
+++ b/src/test/java/com/google/gcloud/RetryParamsTest.java
@@ -62,24 +62,24 @@ public void testSetAndCopy() {
   public void testBadSettings() {
     RetryParams.Builder builder = RetryParams.builder();
     builder.initialRetryDelayMillis(-1);
-    builder = verifyFailure(builder);
+    builder = assertFailure(builder);
     builder.maxRetryDelayMillis(RetryParams.getDefaultInstance().getInitialRetryDelayMillis() - 1);
-    builder = verifyFailure(builder);
+    builder = assertFailure(builder);
     builder.retryDelayBackoffFactor(-1);
-    builder = verifyFailure(builder);
+    builder = assertFailure(builder);
     builder.retryMinAttempts(-1);
-    builder = verifyFailure(builder);
+    builder = assertFailure(builder);
     builder.retryMaxAttempts(RetryParams.getDefaultInstance().getRetryMinAttempts() - 1);
-    builder = verifyFailure(builder);
+    builder = assertFailure(builder);
     builder.totalRetryPeriodMillis(-1);
-    builder = verifyFailure(builder);
+    builder = assertFailure(builder);
     // verify that it is OK for min and max to be equal
     builder.retryMaxAttempts(RetryParams.getDefaultInstance().getRetryMinAttempts());
     builder.maxRetryDelayMillis(RetryParams.getDefaultInstance().getInitialRetryDelayMillis());
     builder.build();
   }
 
-  private static Builder verifyFailure(Builder builder) {
+  private static Builder assertFailure(Builder builder) {
     try {
       builder.build();
       fail("Expected IllegalArgumentException");

From 0358d21fc51205df26fd6c76a8e92ee916ef45f6 Mon Sep 17 00:00:00 2001
From: aozarov 
Date: Fri, 9 Jan 2015 18:47:26 -0800
Subject: [PATCH 086/771] Provide an option for an add with auto-id allocation.

---
 .../google/gcloud/datastore/BatchWriter.java  |  14 +-
 .../gcloud/datastore/BatchWriterImpl.java     |  18 ++
 .../gcloud/datastore/DatastoreHelper.java     |  33 ++--
 .../gcloud/datastore/DatastoreReader.java     |   3 +-
 .../gcloud/datastore/DatastoreService.java    |  28 ++-
 .../datastore/DatastoreServiceException.java  |   2 +-
 .../datastore/DatastoreServiceImpl.java       | 175 ++++++++++++------
 .../gcloud/datastore/PartialEntity.java       |   4 +
 .../gcloud/datastore/StructuredQuery.java     |   5 +-
 .../google/gcloud/datastore/Transaction.java  |   2 +-
 .../gcloud/datastore/TransactionImpl.java     |  10 +-
 .../com/google/gcloud/RetryHelperTest.java    |   8 +-
 .../DatastoreServiceIntegrationTest.java      |  63 +++++--
 .../gcloud/datastore/SerializationTest.java   |   3 +-
 14 files changed, 264 insertions(+), 104 deletions(-)

diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriter.java b/src/main/java/com/google/gcloud/datastore/BatchWriter.java
index d54bc0d51086..626db5aee7de 100644
--- a/src/main/java/com/google/gcloud/datastore/BatchWriter.java
+++ b/src/main/java/com/google/gcloud/datastore/BatchWriter.java
@@ -28,10 +28,20 @@ public interface BatchWriter extends DatastoreWriter {
   @Override
   void add(Entity... entity);
 
+  /**
+   * Datastore add operation.
+   * This method will automatically allocate id for any entity with incomplete key.
+   *
+   * @throws IllegalArgumentException if any of the given entities is missing a key
+   * @throws DatastoreServiceException if a given entity with a complete key was already added to
+   *     this batch or if batch is no longer active
+   */
+  void add(PartialEntity... entity);
+
   /**
    * {@inheritDoc}
    * This operation will be converted to {@link #put} operation for entities that were already
-   *     added or put in this batch.
+   *     added or put in this batch
    * @throws DatastoreServiceException if an entity is marked for deletion in this batch or if
    *     batch is no longer active
    */
@@ -41,7 +51,7 @@ public interface BatchWriter extends DatastoreWriter {
   /**
    * {@inheritDoc}
    * This operation will also remove from this batch any prior writes for entities with the same
-   *     keys.
+   *     keys
    * @throws DatastoreServiceException if batch is no longer active
    */
   @Override
diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
index 273d46f254a3..fc41a2fb25e0 100644
--- a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java
@@ -7,12 +7,15 @@
 
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 class BatchWriterImpl implements BatchWriter {
 
   private final Map toAdd = new LinkedHashMap<>();
+  private final List toAddAutoId = new LinkedList<>();
   private final Map toUpdate = new LinkedHashMap<>();
   private final Map toPut = new LinkedHashMap<>();
   private final Set toDelete = new LinkedHashSet<>();
@@ -59,6 +62,18 @@ public void add(Entity... entities) {
     }
   }
 
+  @Override
+  public void add(PartialEntity... entities) {
+    validateActive();
+    for (PartialEntity entity : entities) {
+      if (entity instanceof Entity) {
+        add((Entity) entity);
+      } else {
+        toAddAutoId.add(entity);
+      }
+    }
+  }
+
   @Override
   public void update(Entity... entities) {
     validateActive();
@@ -103,6 +118,9 @@ public void delete(Key... keys) {
   public void submit() {
     validateActive();
     DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
+    for (PartialEntity entity : toAddAutoId) {
+      mutationPb.addInsertAutoId(entity.toPb());
+    }
     for (Entity entity : toAdd.values()) {
       mutationPb.addInsert(entity.toPb());
     }
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java b/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java
index 7fe4aeda0d96..f4b5b26f69d8 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java
@@ -10,7 +10,6 @@
 /**
  * Adds some functionality to DatastoreService that should
  * be provided statically to the interface (Java 8).
- *
  */
 public class DatastoreHelper implements DatastoreService {
 
@@ -26,8 +25,8 @@ public Entity get(Key key) {
   }
 
   @Override
-  public Iterator get(Key key, Key... others) {
-    return delegate.get(key, others);
+  public Iterator get(Key... key) {
+    return delegate.get(key);
   }
 
   @Override
@@ -56,8 +55,18 @@ public Key allocateId(PartialKey key) {
   }
 
   @Override
-  public Iterator allocateId(PartialKey key, PartialKey... others) {
-    return delegate.allocateId(key, others);
+  public List allocateId(PartialKey... key) {
+    return delegate.allocateId(key);
+  }
+
+  @Override
+  public Entity add(PartialEntity entity) {
+    return delegate.add(entity);
+  }
+
+  @Override
+  public List add(PartialEntity... entity) {
+    return delegate.add(entity);
   }
 
   @Override
@@ -91,17 +100,17 @@ public KeyFactory newKeyFactory() {
    * Returns a list with a value for each given key (ordered by input).
    * A {@code null} would be returned for non-existing keys.
    */
-  public List fetch(Key key, Key... others) {
-    Iterator entities = delegate.get(key, others);
-    Map map = Maps.newHashMapWithExpectedSize(1 + others.length);
+  public List fetch(Key... keys) {
+    Iterator entities = delegate.get(keys);
+    Map map = Maps.newHashMapWithExpectedSize(keys.length);
     while (entities.hasNext()) {
       Entity entity = entities.next();
       map.put(entity.key(), entity);
     }
-    List list = new ArrayList<>(1 + others.length);
-    list.add(map.get(key));
-    for (Key other : others) {
-      list.add(map.get(other));
+    List list = new ArrayList<>(keys.length);
+    for (Key key : keys) {
+      // this will include nulls for non-existing keys
+      list.add(map.get(key));
     }
     return list;
   }
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java
index ea33c0104388..29a975bda442 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java
@@ -22,8 +22,9 @@ public interface DatastoreReader {
    * {@link Iterator#next next} methods.
    *
    * @throws DatastoreServiceException upon failure.
+   * @see #get(Key)
    */
-  Iterator get(Key key, Key... others);
+  Iterator get(Key... key);
 
   /**
    * Submit a {@link Query} and returns its result.
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java
index 48ad05fe5713..fb33382af74a 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java
@@ -1,6 +1,6 @@
 package com.google.gcloud.datastore;
 
-import java.util.Iterator;
+import java.util.List;
 
 /**
  * An interface for Google Cloud Datastore dataset.
@@ -37,10 +37,34 @@ public interface DatastoreService extends DatastoreReaderWriter {
   /**
    * Returns a list of keys using the allocated ids ordered by the input.
    *
+   * @throws DatastoreServiceException upon failure
    * @see #allocateId(PartialKey)
+   */
+  List allocateId(PartialKey... key);
+
+  /**
+   * Datastore add operation.
+   * This method will automatically allocate an id if necessary.
+   *
+   * @param entity the entity to add
+   * @return an {@code Entity} with the same properties and a key that is either newly allocated
+   *     or the same one if was already complete
+   * @throws DatastoreServiceException upon failure
+   * @throws IllegalArgumentException if the given entity is missing a key
+   */
+  Entity add(PartialEntity entity);
+
+  /**
+   * Datastore add operation.
+   * This method will automatically allocate id for any entity with incomplete key.
+   *
+   * @return a list of {@code Entity} ordered by input with the same properties and a key that is
+   *     either newly allocated or the same one if was already complete
    * @throws DatastoreServiceException upon failure
+   * @throws IllegalArgumentException if any of the given entities is missing a key
+   * @see #add(PartialKey)
    */
-  Iterator allocateId(PartialKey key, PartialKey... others);
+  List add(PartialEntity... entity);
 
   /**
    * {@inheritDoc}
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java
index d5f36ebd83ab..b62df718fa61 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java
@@ -114,7 +114,7 @@ static DatastoreServiceException translateAndThrow(DatastoreException exception)
     String reason = "";
     if (message != null) {
       try {
-        JSONObject json = new JSONObject(new JSONTokener(exception.getMessage()));
+        JSONObject json = new JSONObject(new JSONTokener(message));
         JSONObject error = json.getJSONObject("error").getJSONArray("errors").getJSONObject(0);
         reason = error.getString("reason");
         message = error.getString("message");
diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
index a68783c95609..2dd1a482d299 100644
--- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java
@@ -3,8 +3,13 @@
 import com.google.api.services.datastore.DatastoreV1;
 import com.google.api.services.datastore.client.Datastore;
 import com.google.api.services.datastore.client.DatastoreException;
+import com.google.common.base.Function;
 import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
 import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Sets;
 import com.google.gcloud.ExceptionHandler;
 import com.google.gcloud.RetryHelper;
 import com.google.gcloud.RetryHelper.RetryHelperException;
@@ -12,9 +17,11 @@
 import com.google.protobuf.ByteString;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Callable;
@@ -22,8 +29,6 @@
 
 final class DatastoreServiceImpl implements DatastoreService {
 
-  static final Key[] EMPTY_KEY_ARRAY = {};
-  static final PartialKey[] EMPTY_PARTIAL_KEY_ARRAY = {};
   private static final ExceptionHandler.Interceptor EXCEPTION_HANDLER_INTERCEPTOR =
       new ExceptionHandler.Interceptor() {
 
@@ -97,27 +102,29 @@ DatastoreV1.RunQueryResponse runQuery(final DatastoreV1.RunQueryRequest requestP
 
   @Override
   public Key allocateId(PartialKey key) {
-    return allocateId(key, EMPTY_PARTIAL_KEY_ARRAY).next();
+    return allocateId(new PartialKey[]{key}).get(0);
   }
 
   @Override
-  public Iterator allocateId(PartialKey key, PartialKey... others) {
+  public List allocateId(PartialKey... keys) {
+    if (keys.length == 0) {
+      return Collections.emptyList();
+    }
     DatastoreV1.AllocateIdsRequest.Builder requestPb = DatastoreV1.AllocateIdsRequest.newBuilder();
-    requestPb.addKey(trimNameOrId(key).toPb());
-    for (PartialKey other : others) {
-      requestPb.addKey(trimNameOrId(other).toPb());
+    for (PartialKey key : keys) {
+      requestPb.addKey(trimNameOrId(key).toPb());
     }
     // TODO(ozarov): will need to populate "force" after b/18594027 is fixed.
     DatastoreV1.AllocateIdsResponse responsePb = allocateIds(requestPb.build());
-    final Iterator keys = responsePb.getKeyList().iterator();
-    return new AbstractIterator() {
-      @Override protected Key computeNext() {
-        if (keys.hasNext()) {
-          return Key.fromPb(keys.next());
-        }
-        return endOfData();
-      }
-    };
+    Iterator keyIterator = responsePb.getKeyList().iterator();
+    ImmutableList.Builder builder = ImmutableList.builder().addAll(
+        Iterators.transform(keyIterator, new Function() {
+          @Override
+          public Key apply(DatastoreV1.Key keyPb) {
+            return Key.fromPb(keyPb);
+          }
+        }));
+    return builder.build();
   }
 
   DatastoreV1.AllocateIdsResponse allocateIds(final DatastoreV1.AllocateIdsRequest requestPb) {
@@ -139,26 +146,71 @@ private PartialKey trimNameOrId(PartialKey key) {
     return key;
   }
 
+  @Override
+  public Entity add(PartialEntity entity) {
+    return add(new PartialEntity[] {entity}).get(0);
+  }
+
+  @Override
+  public List add(PartialEntity... entities) {
+    if (entities.length == 0) {
+      return Collections.emptyList();
+    }
+    DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
+    Map completeEntities = new LinkedHashMap<>();
+    for (PartialEntity entity : entities) {
+      Entity completeEntity = null;
+      if (entity instanceof  Entity) {
+        completeEntity = (Entity) entity;
+      } else if (entity.key() instanceof Key) {
+        completeEntity = entity.toEntity((Key) entity.key());
+      }
+      if (completeEntity != null) {
+        if (completeEntities.put(completeEntity.key(), completeEntity) != null) {
+          throw DatastoreServiceException.throwInvalidRequest(
+              "Duplicate entity with the key %s", entity.key());
+        }
+        mutationPb.addInsert(completeEntity.toPb());
+      } else {
+        Preconditions.checkArgument(entity.hasKey(), "entity %s is missing a key", entity);
+        mutationPb.addInsertAutoId(entity.toPb());
+      }
+    }
+    DatastoreV1.CommitResponse commitResponse = commitMutation(mutationPb);
+    Iterator allocatedKeys =
+        commitResponse.getMutationResult().getInsertAutoIdKeyList().iterator();
+    ImmutableList.Builder responseBuilder = ImmutableList.builder();
+    for (PartialEntity entity : entities) {
+      PartialKey key = entity.key();
+      Entity completeEntity = completeEntities.get(key);
+      if (completeEntity != null) {
+        responseBuilder.add(completeEntity);
+      } else {
+        responseBuilder.add(entity.toEntity(Key.fromPb(allocatedKeys.next())));
+      }
+    }
+    return responseBuilder.build();
+  }
+
   @Override
   public Entity get(Key key) {
-    Iterator iter = get(key, EMPTY_KEY_ARRAY);
-    return iter.hasNext() ? iter.next() : null;
+    return Iterators.getNext(get(new Key[]{key}), null);
   }
 
   @Override
-  public Iterator get(Key key, Key... others) {
-    return get(null, key, others);
+  public Iterator get(Key... keys) {
+    return get(null, keys);
   }
 
-  Iterator get(DatastoreV1.ReadOptions readOptionsPb, final Key key, final Key... others) {
+  Iterator get(DatastoreV1.ReadOptions readOptionsPb, final Key... keys) {
+    if (keys.length == 0) {
+      return Collections.emptyIterator();
+    }
     DatastoreV1.LookupRequest.Builder requestPb = DatastoreV1.LookupRequest.newBuilder();
     if (readOptionsPb != null) {
       requestPb.setReadOptions(readOptionsPb);
     }
-    LinkedHashSet dedupKeys = new LinkedHashSet<>();
-    dedupKeys.add(key);
-    dedupKeys.addAll(Arrays.asList(others));
-    for (Key k : dedupKeys) {
+    for (Key k : Sets.newLinkedHashSet(Arrays.asList(keys))) {
       requestPb.addKey(k.toPb());
     }
     return new ResultsIterator(requestPb);
@@ -212,62 +264,70 @@ DatastoreV1.LookupResponse lookup(final DatastoreV1.LookupRequest requestPb) {
 
   @Override
     public void add(Entity... entities) {
-    DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
-    Set keys = new LinkedHashSet<>();
-    for (Entity entity : entities) {
-      if (!keys.add(entity.key())) {
-        throw DatastoreServiceException.throwInvalidRequest(
-            "Duplicate entity with the key %s", entity.key());
+    if (entities.length > 0) {
+      DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
+      Set keys = new LinkedHashSet<>();
+      for (Entity entity : entities) {
+        if (!keys.add(entity.key())) {
+          throw DatastoreServiceException.throwInvalidRequest(
+              "Duplicate entity with the key %s", entity.key());
+        }
+        mutationPb.addInsert(entity.toPb());
       }
-      mutationPb.addInsert(entity.toPb());
+      commitMutation(mutationPb);
     }
-    commitMutation(mutationPb);
   }
 
   @Override
   public void update(Entity... entities) {
-    DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
-    Map dedupEntities = new LinkedHashMap<>();
-    for (Entity entity : entities) {
-      dedupEntities.put(entity.key(), entity);
-    }
-    for (Entity entity : dedupEntities.values()) {
-      mutationPb.addUpdate(entity.toPb());
+    if (entities.length > 0) {
+      DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
+      Map dedupEntities = new LinkedHashMap<>();
+      for (Entity entity : entities) {
+        dedupEntities.put(entity.key(), entity);
+      }
+      for (Entity entity : dedupEntities.values()) {
+        mutationPb.addUpdate(entity.toPb());
+      }
+      commitMutation(mutationPb);
     }
-    commitMutation(mutationPb);
   }
 
   @Override
   public void put(Entity... entities) {
-    DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
-    Map dedupEntities = new LinkedHashMap<>();
-    for (Entity entity : entities) {
-      dedupEntities.put(entity.key(), entity);
-    }
-    for (Entity e : dedupEntities.values()) {
-      mutationPb.addUpsert(e.toPb());
+    if (entities.length > 0) {
+      DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
+      Map dedupEntities = new LinkedHashMap<>();
+      for (Entity entity : entities) {
+        dedupEntities.put(entity.key(), entity);
+      }
+      for (Entity e : dedupEntities.values()) {
+        mutationPb.addUpsert(e.toPb());
+      }
+      commitMutation(mutationPb);
     }
-    commitMutation(mutationPb);
   }
 
   @Override
   public void delete(Key... keys) {
-    DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
-    Set dedupKeys = new LinkedHashSet<>(Arrays.asList(keys));
-    for (Key key : dedupKeys) {
-      mutationPb.addDelete(key.toPb());
+    if (keys.length > 0) {
+      DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder();
+      Set dedupKeys = new LinkedHashSet<>(Arrays.asList(keys));
+      for (Key key : dedupKeys) {
+        mutationPb.addDelete(key.toPb());
+      }
+      commitMutation(mutationPb);
     }
-    commitMutation(mutationPb);
   }
 
-  private void commitMutation(DatastoreV1.Mutation.Builder mutationPb) {
+  private DatastoreV1.CommitResponse commitMutation(DatastoreV1.Mutation.Builder mutationPb) {
     if (options.force()) {
       mutationPb.setForce(true);
     }
     DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder();
     requestPb.setMode(DatastoreV1.CommitRequest.Mode.NON_TRANSACTIONAL);
     requestPb.setMutation(mutationPb);
-    commit(requestPb.build());
+    return commit(requestPb.build());
   }
 
   DatastoreV1.CommitResponse commit(final DatastoreV1.CommitRequest requestPb) {
@@ -290,7 +350,8 @@ DatastoreV1.BeginTransactionResponse beginTransaction(
       final DatastoreV1.BeginTransactionRequest requestPb) {
     try {
       return RetryHelper.runWithRetries(new Callable() {
-        @Override public DatastoreV1.BeginTransactionResponse call() throws DatastoreException {
+        @Override
+        public DatastoreV1.BeginTransactionResponse call() throws DatastoreException {
           return datastore.beginTransaction(requestPb);
         }
       }, retryParams, EXCEPTION_HANDLER);
diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java
index 4f3a71955455..c1dead077d57 100644
--- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java
+++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java
@@ -55,6 +55,10 @@ public Entity toEntity(Key key) {
     return new Entity(key, ImmutableSortedMap.copyOf(properties()));
   }
 
+  public boolean hasKey() {
+    return key != null;
+  }
+
   /**
    * Returns the key for this entity or {@code null} if it does not have one.
    */
diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
index c02e7e5fc91a..671e7403b511 100644
--- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
+++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java
@@ -868,7 +868,7 @@ public String kind() {
   }
 
   protected boolean keyOnly() {
-    return projection.size() == 1 && projection.get(0).property.equals(KEY_PROPERTY_NAME);
+    return projection.size() == 1 && KEY_PROPERTY_NAME.equals(projection.get(0).property);
   }
 
   List projection() {
@@ -963,7 +963,8 @@ protected Object fromPb(Type type, String namespace, byte[] bytesPb)
     return fromPb(type, namespace, DatastoreV1.Query.parseFrom(bytesPb));
   }
 
-  private static StructuredQuery fromPb(Type type, String namespace, DatastoreV1.Query queryPb) {
+  private static StructuredQuery fromPb(Type type, String namespace,
+      DatastoreV1.Query queryPb) {
     BaseBuilder builder;
     if (type.equals(Type.FULL)) {
       builder = builder();
diff --git a/src/main/java/com/google/gcloud/datastore/Transaction.java b/src/main/java/com/google/gcloud/datastore/Transaction.java
index 06c4e5a45997..21a41f896bf7 100644
--- a/src/main/java/com/google/gcloud/datastore/Transaction.java
+++ b/src/main/java/com/google/gcloud/datastore/Transaction.java
@@ -55,7 +55,7 @@ public interface Transaction extends DatastoreReaderWriter {
    * @throws DatastoreServiceException upon failure or if no longer active
    */
   @Override
-  Iterator get(Key key, Key... others);
+  Iterator get(Key... key);
 
   /**
    * {@inheritDoc}
diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java
index 44b231b81383..d88065eca35d 100644
--- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java
+++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java
@@ -3,6 +3,7 @@
 import static com.google.gcloud.datastore.DatastoreServiceException.throwInvalidRequest;
 
 import com.google.api.services.datastore.DatastoreV1;
+import com.google.common.collect.Iterators;
 import com.google.gcloud.datastore.TransactionOption.IsolationLevel;
 import com.google.protobuf.ByteString;
 
@@ -11,7 +12,7 @@
 import java.util.List;
 import java.util.Map;
 
-public final class TransactionImpl extends BatchWriterImpl implements Transaction {
+final class TransactionImpl extends BatchWriterImpl implements Transaction {
 
   private final ByteString transaction;
   private boolean wasRolledback;
@@ -42,16 +43,15 @@ private static BatchWriteOption[] getBatchOptions(TransactionOption... options)
 
   @Override
   public Entity get(Key key) {
-    Iterator iter = get(key, DatastoreServiceImpl.EMPTY_KEY_ARRAY);
-    return iter.hasNext() ? iter.next() : null;
+    return Iterators.getNext(get(new Key[] {key}), null);
   }
 
   @Override
-  public Iterator get(Key key, Key... others) {
+  public Iterator get(Key... keys) {
     validateActive();
     DatastoreV1.ReadOptions.Builder readOptionsPb = DatastoreV1.ReadOptions.newBuilder();
     readOptionsPb.setTransaction(transaction);
-    return datastore.get(readOptionsPb.build(), key, others);
+    return datastore.get(readOptionsPb.build(), keys);
   }
 
   @Override
diff --git a/src/test/java/com/google/gcloud/RetryHelperTest.java b/src/test/java/com/google/gcloud/RetryHelperTest.java
index d22ba9bcc43b..7f89d5e654a6 100644
--- a/src/test/java/com/google/gcloud/RetryHelperTest.java
+++ b/src/test/java/com/google/gcloud/RetryHelperTest.java
@@ -85,9 +85,11 @@ class E4Exception extends E2Exception {
     }
 
     params = RetryParams.builder().initialRetryDelayMillis(0).retryMaxAttempts(5).build();
-    handler = ExceptionHandler.builder().retryOn(E1Exception.class, E4Exception.class).abortOn(E3Exception.class).build();
-    final Iterator exceptions =
-        Arrays.asList(new E1Exception(), new E2Exception(), new E4Exception(), new E3Exception()).iterator();
+    handler = ExceptionHandler.builder()
+        .retryOn(E1Exception.class, E4Exception.class)
+        .abortOn(E3Exception.class).build();
+    final Iterator exceptions = Arrays.asList(
+        new E1Exception(), new E2Exception(), new E4Exception(), new E3Exception()).iterator();
     try {
       RetryHelper.runWithRetries(new Callable() {
         @Override public Void call() throws E1Exception {
diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
index 0dfad2ebf791..5fc4949a3748 100644
--- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
+++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java
@@ -15,7 +15,6 @@
 import com.google.gcloud.datastore.StructuredQuery.OrderBy;
 import com.google.gcloud.datastore.StructuredQuery.Projection;
 import com.google.gcloud.datastore.StructuredQuery.PropertyFilter;
-
 import org.easymock.EasyMock;
 import org.junit.After;
 import org.junit.Before;
@@ -25,10 +24,8 @@
 
 import java.io.IOException;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 
 @RunWith(JUnit4.class)
 public class DatastoreServiceIntegrationTest {
@@ -250,7 +247,10 @@ public void testNewBatchWriter() {
     Entity entity5 = Entity.builder(KEY5).set("value", "value").build();
 
     batchWriter.add(entity4, entity5);
+    batchWriter.add(PARTIAL_ENTITY1);
     batchWriter.put(ENTITY3, entity1, entity2);
+    // TODO(OZAROV): CHANGE SUBMIT TO RETURN A BATCH RESPONSE WITH GENERATED KEYS
+    // AND USE THE KEY TO VALIDATE THE WRITE
     batchWriter.submit();
     Iterator entities =
         helper.fetch(KEY1, KEY2, KEY3, entity4.key(), entity5.key()).iterator();
@@ -474,20 +474,15 @@ public void testAllocateIdArray() {
     PartialKey partialKey2 = keyFactory.kind(KIND2).ancestors(PathElement.of(KIND1, 10)).newKey();
     Key key3 = keyFactory.newKey("name");
     Key key4 = keyFactory.newKey(1);
-    Iterator result =
+    List result =
         datastore.allocateId(partialKey1, partialKey2, key3, key4, partialKey1, key3);
-    Map map = new HashMap<>();
-    int count = 0;
-    while (result.hasNext()) {
-      map.put(++count, result.next());
-    }
-    assertEquals(6, map.size());
-    assertEquals(partialKey1.newKey(map.get(1).id()), map.get(1));
-    assertEquals(partialKey1.newKey(map.get(5).id()), map.get(5));
-    assertEquals(partialKey2.newKey(map.get(2).id()), map.get(2));
-    assertEquals(Key.builder(key3).id(map.get(3).id()).build(), map.get(3));
-    assertEquals(Key.builder(key3).id(map.get(6).id()).build(), map.get(6));
-    assertEquals(Key.builder(key4).id(map.get(4).id()).build(), map.get(4));
+    assertEquals(6, result.size());
+    assertEquals(partialKey1.newKey(result.get(0).id()), result.get(0));
+    assertEquals(partialKey1.newKey(result.get(4).id()), result.get(4));
+    assertEquals(partialKey2.newKey(result.get(1).id()), result.get(1));
+    assertEquals(Key.builder(key3).id(result.get(2).id()).build(), result.get(2));
+    assertEquals(Key.builder(key3).id(result.get(5).id()).build(), result.get(5));
+    assertEquals(Key.builder(key4).id(result.get(3).id()).build(), result.get(3));
   }
 
   @Test
@@ -542,7 +537,7 @@ public void testGetArray() {
   }
 
   @Test
-  public void testAdd() {
+  public void testAddEntity() {
     List keys = helper.fetch(ENTITY1.key(), ENTITY3.key());
     assertEquals(ENTITY1, keys.get(0));
     assertNull(keys.get(1));
@@ -558,6 +553,40 @@ public void testAdd() {
     assertEquals(ENTITY3, datastore.get(ENTITY3.key()));
   }
 
+  @Test
+  public void testAddPartialEntity() {
+    List keys = helper.fetch(ENTITY1.key(), ENTITY3.key());
+    assertEquals(ENTITY1, keys.get(0));
+    assertNull(keys.get(1));
+    assertEquals(2, keys.size());
+
+    try {
+      datastore.add(ENTITY1);
+      fail("Expecting a failure");
+    } catch (DatastoreServiceException expected) {
+      // expected;
+    }
+
+    PartialEntity pe = PartialEntity.builder(PARTIAL_ENTITY2).key(KEY5).build();
+    List response =
+        datastore.add(PARTIAL_ENTITY1, PARTIAL_ENTITY2, ENTITY3, PARTIAL_ENTITY1, pe);
+    assertEquals(5, response.size());
+    assertEquals(PARTIAL_ENTITY1.properties(), response.get(0).properties());
+    assertEquals(PARTIAL_ENTITY1.properties(), datastore.get(response.get(0).key()).properties());
+    assertEquals(PARTIAL_ENTITY2.properties(), response.get(1).properties());
+    assertEquals(PARTIAL_ENTITY2.properties(), datastore.get(response.get(1).key()).properties());
+    assertSame(ENTITY3, response.get(2));
+    assertEquals(ENTITY3, datastore.get(response.get(2).key()));
+    assertEquals(PARTIAL_ENTITY1.properties(), response.get(3).properties());
+    assertEquals(PARTIAL_ENTITY1.properties(), datastore.get(response.get(3).key()).properties());
+    assertEquals(pe.properties(), response.get(4).properties());
+    assertEquals(pe.key(), response.get(4).key());
+    assertEquals(pe.properties(), datastore.get(response.get(4).key()).properties());
+    assertEquals(pe.key(), datastore.get(response.get(4).key()).key());
+    assertEquals(pe, response.get(4));
+    assertEquals(datastore.get(response.get(4).key()), response.get(4));
+  }
+
   @Test
   public void testUpdate() {
     List keys = helper.fetch(ENTITY1.key(), ENTITY3.key());
diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
index 706485b4759e..44fb398bdfb6 100644
--- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java
+++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java
@@ -143,7 +143,8 @@ public void testTypes() throws Exception {
   }
 
   @SuppressWarnings("unchecked")
-  private  T serializeAndDeserialize(T obj) throws IOException, ClassNotFoundException {
+  private  T serializeAndDeserialize(T obj)
+      throws IOException, ClassNotFoundException {
     ByteArrayOutputStream bytes = new ByteArrayOutputStream();
     try (ObjectOutputStream output = new ObjectOutputStream(bytes)) {
       output.writeObject(obj);

From 48c131095dc301168daa7de478d7fb1a6eaa73be Mon Sep 17 00:00:00 2001
From: aozarov 
Date: Fri, 9 Jan 2015 18:49:08 -0800
Subject: [PATCH 087/771] add git-demo.iml

---
 git-demo.iml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-demo.iml b/git-demo.iml
index b5c10cc72045..3afdc82b792f 100644
--- a/git-demo.iml
+++ b/git-demo.iml
@@ -10,7 +10,7 @@
       
       
     
-    
+    
     
     
     

From 44f4eb53295a3212d1cbd0c78a69fe50041c1d07 Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Fri, 9 Jan 2015 23:43:20 -0800
Subject: [PATCH 088/771] removing git-demo.iml

---
 .gitignore   |  61 ++++++++++++-
 git-demo.iml | 247 ---------------------------------------------------
 2 files changed, 58 insertions(+), 250 deletions(-)
 delete mode 100644 git-demo.iml

diff --git a/.gitignore b/.gitignore
index e3d713f330e1..7a46a67d0043 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,60 @@
-/target/
-.settings
+.gitignore
+
+*.py[cod]
+*.sw[op]
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+__pycache__
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+nosetests.xml
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
+*.iml
 .idea
+.settings
 .DS_Store
-*.iml
+target
+
+# Built documentation
+docs/_build
+
+# Virtual environment
+env/
+coverage.xml
+
+# Regression test environment variables.
+regression/local_test_setup
+
+# Make sure a generated file isn't accidentally committed.
+pylintrc_reduced
+
+# Wheel directory used in Travis builds.
+gcloud-python-wheels/
diff --git a/git-demo.iml b/git-demo.iml
deleted file mode 100644
index 3afdc82b792f..000000000000
--- a/git-demo.iml
+++ /dev/null
@@ -1,247 +0,0 @@
-
-
-  
-    
-    
-    
-      
-      
-      
-      
-      
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-  
-  
-    
-    
-    
-  
-
\ No newline at end of file

From 98b239194485db4356d3d3ad09147a42002528fc Mon Sep 17 00:00:00 2001
From: ozarov 
Date: Sun, 11 Jan 2015 21:51:07 -0800
Subject: [PATCH 089/771] batch/transaction submit/commit returns generated
 keys

---
 .../com/google/gcloud/datastore/Batch.java    | 30 +++++++
 .../{BatchWriterImpl.java => BatchImpl.java}  | 41 ++++++++--
 ...BatchWriteOption.java => BatchOption.java} | 13 ++-
 .../google/gcloud/datastore/BatchWriter.java  | 79 ------------------
 .../datastore/DatastoreBatchWriter.java       | 82 +++++++++++++++++++
 .../gcloud/datastore/DatastoreHelper.java     |  4 +-
 .../gcloud/datastore/DatastoreService.java    |  5 +-
 .../datastore/DatastoreServiceImpl.java       |  4 +-
 .../google/gcloud/datastore/Transaction.java  | 10 ++-
 .../gcloud/datastore/TransactionImpl.java     | 21 +++--
 .../gcloud/datastore/TransactionOption.java   |  8 +-
 .../DatastoreServiceIntegrationTest.java      | 31 +++----
 12 files changed, 200 insertions(+), 128 deletions(-)
 create mode 100644 src/main/java/com/google/gcloud/datastore/Batch.java
 rename src/main/java/com/google/gcloud/datastore/{BatchWriterImpl.java => BatchImpl.java} (78%)
 rename src/main/java/com/google/gcloud/datastore/{BatchWriteOption.java => BatchOption.java} (61%)
 delete mode 100644 src/main/java/com/google/gcloud/datastore/BatchWriter.java
 create mode 100644 src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java

diff --git a/src/main/java/com/google/gcloud/datastore/Batch.java b/src/main/java/com/google/gcloud/datastore/Batch.java
new file mode 100644
index 000000000000..705cec0b1139
--- /dev/null
+++ b/src/main/java/com/google/gcloud/datastore/Batch.java
@@ -0,0 +1,30 @@
+package com.google.gcloud.datastore;
+
+/**
+ * An interface to represent a batch of write operations.
+ * Any write operation that is applied on a batch will only be sent
+ * to the Datastore upon {@link #submit}.
+ * A usage example:
+ * 
 {@code
+ *   Entity entity1 = datastore.get(key1);
+ *   Batch batch = datastore.newBatch();
+ *   Entity entity2 = Entity.builder(key2).set("name", "John").build();
+ *   entity1 = Entity.builder(entity1).clear().setNull("bla").build();
+ *   Entity entity3 = Entity.builder(key3).set("title", "title").build();
+ *   batch.update(entity1);
+ *   batch.add(entity2, entity3);
+ *   batch.submit();
+ * } 
+ */ +public interface Batch extends DatastoreBatchWriter { + + interface Response extends DatastoreBatchWriter.Response { + } + + /** + * Submit the batch to the Datastore. + * + * @throws DatastoreServiceException if there was any failure or if batch is not longer active + */ + Response submit(); +} diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java b/src/main/java/com/google/gcloud/datastore/BatchImpl.java similarity index 78% rename from src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java rename to src/main/java/com/google/gcloud/datastore/BatchImpl.java index fc41a2fb25e0..57c07cc2741a 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchWriterImpl.java +++ b/src/main/java/com/google/gcloud/datastore/BatchImpl.java @@ -3,7 +3,9 @@ import static com.google.gcloud.datastore.DatastoreServiceException.throwInvalidRequest; import com.google.api.services.datastore.DatastoreV1; -import com.google.gcloud.datastore.BatchWriteOption.ForceWrites; +import com.google.common.base.Function; +import com.google.common.collect.Lists; +import com.google.gcloud.datastore.BatchOption.ForceWrites; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -12,7 +14,7 @@ import java.util.Map; import java.util.Set; -class BatchWriterImpl implements BatchWriter { +class BatchImpl implements Batch { private final Map toAdd = new LinkedHashMap<>(); private final List toAddAutoId = new LinkedList<>(); @@ -24,10 +26,30 @@ class BatchWriterImpl implements BatchWriter { private boolean active = true; - BatchWriterImpl(DatastoreServiceImpl datastore, BatchWriteOption... options) { + + class ResponseImpl implements Response { + + private final DatastoreV1.CommitResponse response; + + public ResponseImpl(DatastoreV1.CommitResponse response) { + this.response = response; + } + + @Override + public List generatedKeys() { + return Lists.transform(response.getMutationResult().getInsertAutoIdKeyList(), + new Function() { + @Override public Key apply(DatastoreV1.Key keyPb) { + return Key.fromPb(keyPb); + } + }); + } + } + + BatchImpl(DatastoreServiceImpl datastore, BatchOption... options) { this.datastore = datastore; - Map, BatchWriteOption> optionsMap = - BatchWriteOption.asImmutableMap(options); + Map, BatchOption> optionsMap = + BatchOption.asImmutableMap(options); if (optionsMap.containsKey(ForceWrites.class)) { force = ((ForceWrites) optionsMap.get(ForceWrites.class)).force(); } else { @@ -115,7 +137,11 @@ public void delete(Key... keys) { } @Override - public void submit() { + public Response submit() { + return new ResponseImpl(commitRequest()); + } + + DatastoreV1.CommitResponse commitRequest() { validateActive(); DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); for (PartialEntity entity : toAddAutoId) { @@ -138,8 +164,9 @@ public void submit() { } DatastoreV1.CommitRequest.Builder requestPb = newCommitRequest(); requestPb.setMutation(mutationPb); - datastore.commit(requestPb.build()); + DatastoreV1.CommitResponse response = datastore.commit(requestPb.build()); active = false; + return response; } @Override diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java b/src/main/java/com/google/gcloud/datastore/BatchOption.java similarity index 61% rename from src/main/java/com/google/gcloud/datastore/BatchWriteOption.java rename to src/main/java/com/google/gcloud/datastore/BatchOption.java index f2bf33266252..efcda4df4645 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchWriteOption.java +++ b/src/main/java/com/google/gcloud/datastore/BatchOption.java @@ -4,11 +4,11 @@ import java.util.Map; -public abstract class BatchWriteOption implements java.io.Serializable { +public abstract class BatchOption implements java.io.Serializable { private static final long serialVersionUID = -3932758377282659839L; - public static final class ForceWrites extends BatchWriteOption { + public static final class ForceWrites extends BatchOption { private static final long serialVersionUID = 2555054296046232799L; @@ -23,7 +23,7 @@ public boolean force() { } } - BatchWriteOption() { + BatchOption() { // package protected } @@ -31,11 +31,10 @@ public static ForceWrites forceWrites() { return new ForceWrites(true); } - static Map, BatchWriteOption> asImmutableMap( - BatchWriteOption... options) { - ImmutableMap.Builder, BatchWriteOption> builder = + static Map, BatchOption> asImmutableMap(BatchOption... options) { + ImmutableMap.Builder, BatchOption> builder = ImmutableMap.builder(); - for (BatchWriteOption option : options) { + for (BatchOption option : options) { builder.put(option.getClass(), option); } return builder.build(); diff --git a/src/main/java/com/google/gcloud/datastore/BatchWriter.java b/src/main/java/com/google/gcloud/datastore/BatchWriter.java deleted file mode 100644 index 626db5aee7de..000000000000 --- a/src/main/java/com/google/gcloud/datastore/BatchWriter.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.google.gcloud.datastore; - -/** - * An interface to represent a batch of write operations. - * Any write operation that is applied on a batch will only be sent - * to the Datastore upon {@link #submit} and with as few RPC calls as possible. - * A usage example: - *
 {@code
- *   Entity entity1 = datastore.get(key1);
- *   BatchWriter batchWriter = datastore.newBatchWriter();
- *   Entity entity2 = Entity.builder(key2).set("name", "John").build();
- *   entity1 = Entity.builder(entity1).clear().setNull("bla").build();
- *   Entity entity3 = Entity.builder(key3).set("title", new StringValue("title")).build();
- *   batchWriter.update(entity1);
- *   batchWriter.add(entity2, entity3);
- *   batchWriter.submit();
- * } 
- */ -public interface BatchWriter extends DatastoreWriter { - - /** - * {@inheritDoc} - * This operation will be converted to {@link #put} operation for entities that were already - * marked for deletion in this batch. - * @throws DatastoreServiceException if a given entity already added to this batch or if batch - * is no longer active - */ - @Override - void add(Entity... entity); - - /** - * Datastore add operation. - * This method will automatically allocate id for any entity with incomplete key. - * - * @throws IllegalArgumentException if any of the given entities is missing a key - * @throws DatastoreServiceException if a given entity with a complete key was already added to - * this batch or if batch is no longer active - */ - void add(PartialEntity... entity); - - /** - * {@inheritDoc} - * This operation will be converted to {@link #put} operation for entities that were already - * added or put in this batch - * @throws DatastoreServiceException if an entity is marked for deletion in this batch or if - * batch is no longer active - */ - @Override - void update(Entity... entity); - - /** - * {@inheritDoc} - * This operation will also remove from this batch any prior writes for entities with the same - * keys - * @throws DatastoreServiceException if batch is no longer active - */ - @Override - void delete(Key... key); - - /** - * {@inheritDoc} - * This operation will also remove from this batch any prior writes for the same entities. - * @throws DatastoreServiceException if batch is no longer active - */ - @Override - void put(Entity... entity); - - /** - * Submit the batch to the Datastore. - * - * @throws DatastoreServiceException if there was any failure or if batch is not longer active - */ - void submit(); - - /** - * Returns {@code true} if batch is still active (was not submitted). - */ - boolean active(); -} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java b/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java new file mode 100644 index 000000000000..25c2409e58ea --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java @@ -0,0 +1,82 @@ +/* + * 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.datastore; + +import java.util.List; + +/** + * An interface to represent a batch of write operations. + * All write operation for a batch writer will be applied to the Datastore in one RPC call. + */ +interface DatastoreBatchWriter extends DatastoreWriter { + + interface Response { + List generatedKeys(); + } + + /** + * {@inheritDoc} + * This operation will be converted to {@link #put} operation for entities that were already + * marked for deletion in this writer. + * @throws com.google.gcloud.datastore.DatastoreServiceException if a given entity already added + * to this writer or if not active + */ + @Override + void add(Entity... entity); + + /** + * Datastore add operation. + * This method will automatically allocate id for any entity with an incomplete key. + * + * @throws IllegalArgumentException if any of the given entities is missing a key + * @throws com.google.gcloud.datastore.DatastoreServiceException if a given entity with a + * complete key was already added to this writer or if not active + */ + void add(PartialEntity... entity); + + /** + * {@inheritDoc} + * This operation will be converted to {@link #put} operation for entities that were already + * added or put in this writer + * @throws com.google.gcloud.datastore.DatastoreServiceException if an entity is marked for + * deletion in this writer or if not active + */ + @Override + void update(Entity... entity); + + /** + * {@inheritDoc} + * This operation will also remove from this batch any prior writes for entities with the same + * keys + * @throws com.google.gcloud.datastore.DatastoreServiceException if not active + */ + @Override + void delete(Key... key); + + /** + * {@inheritDoc} + * This operation will also remove from this writer any prior writes for the same entities. + * @throws com.google.gcloud.datastore.DatastoreServiceException if not active + */ + @Override + void put(Entity... entity); + + /** + * Returns {@code true} if still active (write operations were not sent to the Datastore). + */ + boolean active(); +} diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java b/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java index f4b5b26f69d8..453c3e8c6535 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java @@ -45,8 +45,8 @@ public Transaction newTransaction(TransactionOption... options) { } @Override - public BatchWriter newBatchWriter(BatchWriteOption... options) { - return delegate.newBatchWriter(options); + public Batch newBatch(BatchOption... options) { + return delegate.newBatch(options); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index fb33382af74a..aba23f36a2d2 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -20,10 +20,9 @@ public interface DatastoreService extends DatastoreReaderWriter { Transaction newTransaction(TransactionOption... options); /** - * Returns a new Batch writer for processing multiple write operations - * in one request. + * Returns a new Batch for processing multiple write operations in one request. */ - BatchWriter newBatchWriter(BatchWriteOption... options); + Batch newBatch(BatchOption... options); /** * Allocate a unique id for the given key. diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index 2dd1a482d299..ae5b5619e62f 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -70,8 +70,8 @@ public DatastoreServiceOptions options() { } @Override - public BatchWriter newBatchWriter(BatchWriteOption... options) { - return new BatchWriterImpl(this, options); + public Batch newBatch(BatchOption... options) { + return new BatchImpl(this, options); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/Transaction.java b/src/main/java/com/google/gcloud/datastore/Transaction.java index 21a41f896bf7..fea662ead76a 100644 --- a/src/main/java/com/google/gcloud/datastore/Transaction.java +++ b/src/main/java/com/google/gcloud/datastore/Transaction.java @@ -4,7 +4,7 @@ /** * A Google cloud datastore transaction. - * Any write operation that is applied on a transaction will only be sent + * Similar to {@link Batch} any write operation that is applied on a transaction will only be sent * to the Datastore upon {@link #commit}. A call to {@link #rollback} will invalidate * the transaction and discard the changes. Any read operation that is done by a transaction * will be part of it and therefore a {@code commit} is guaranteed to fail if an entity @@ -33,7 +33,10 @@ * @see Google Cloud Datastore transactions * */ -public interface Transaction extends DatastoreReaderWriter { +public interface Transaction extends DatastoreBatchWriter, DatastoreReaderWriter { + + interface Response extends DatastoreBatchWriter.Response { + } /** * {@inheritDoc} @@ -74,7 +77,7 @@ public interface Transaction extends DatastoreReaderWriter { * * @throws DatastoreServiceException if could not commit the transaction or if no longer active */ - void commit(); + Response commit(); /** * Rollback the transaction. @@ -86,5 +89,6 @@ public interface Transaction extends DatastoreReaderWriter { /** * Returns {@code true} if the transaction is still active (was not committed or rolledback). */ + @Override boolean active(); } diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java index d88065eca35d..c2b4b3c9d5ae 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java @@ -12,11 +12,18 @@ import java.util.List; import java.util.Map; -final class TransactionImpl extends BatchWriterImpl implements Transaction { +final class TransactionImpl extends BatchImpl implements Transaction { private final ByteString transaction; private boolean wasRolledback; + class ResponseImpl extends BatchImpl.ResponseImpl implements Transaction.Response { + + public ResponseImpl(DatastoreV1.CommitResponse response) { + super(response); + } + } + TransactionImpl(DatastoreServiceImpl datastore, TransactionOption... options) { super(datastore, getBatchOptions(options)); DatastoreV1.BeginTransactionRequest.Builder requestPb = @@ -30,15 +37,15 @@ final class TransactionImpl extends BatchWriterImpl implements Transaction { transaction = datastore.requestTransactionId(requestPb); } - private static BatchWriteOption[] getBatchOptions(TransactionOption... options) { - List batchOptions = new ArrayList<>(options.length); + private static BatchOption[] getBatchOptions(TransactionOption... options) { + List batchOptions = new ArrayList<>(options.length); for (TransactionOption option : options) { - BatchWriteOption batchOption = option.toBatchWriteOption(); + BatchOption batchOption = option.toBatchWriteOption(); if (batchOption != null) { batchOptions.add(batchOption); } } - return batchOptions.toArray(new BatchWriteOption[batchOptions.size()]); + return batchOptions.toArray(new BatchOption[batchOptions.size()]); } @Override @@ -63,8 +70,8 @@ public QueryResult run(Query query) { } @Override - public void commit() { - submit(); + public Transaction.Response commit() { + return new ResponseImpl(commitRequest()); } @Override diff --git a/src/main/java/com/google/gcloud/datastore/TransactionOption.java b/src/main/java/com/google/gcloud/datastore/TransactionOption.java index 9de8ea8ff745..433787a02f69 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionOption.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionOption.java @@ -26,8 +26,8 @@ public boolean force() { } @Override - BatchWriteOption toBatchWriteOption() { - return new BatchWriteOption.ForceWrites(force); + BatchOption toBatchWriteOption() { + return new BatchOption.ForceWrites(force); } } @@ -63,7 +63,7 @@ public Level level() { } @Override - BatchWriteOption toBatchWriteOption() { + BatchOption toBatchWriteOption() { return null; } } @@ -94,5 +94,5 @@ static Map, TransactionOption> asImmutableMap return builder.build(); } - abstract BatchWriteOption toBatchWriteOption(); + abstract BatchOption toBatchWriteOption(); } diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java index 5fc4949a3748..ec40a29535b8 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java @@ -239,19 +239,18 @@ private void verifyNotUsable(DatastoreWriter writer) { } @Test - public void testNewBatchWriter() { - BatchWriter batchWriter = datastore.newBatchWriter(); + public void testNewBatch() { + Batch batch = datastore.newBatch(); Entity entity1 = Entity.builder(ENTITY1).clear().build(); Entity entity2 = Entity.builder(ENTITY2).clear().setNull("bla").build(); Entity entity4 = Entity.builder(KEY4).set("value", StringValue.of("value")).build(); Entity entity5 = Entity.builder(KEY5).set("value", "value").build(); - batchWriter.add(entity4, entity5); - batchWriter.add(PARTIAL_ENTITY1); - batchWriter.put(ENTITY3, entity1, entity2); - // TODO(OZAROV): CHANGE SUBMIT TO RETURN A BATCH RESPONSE WITH GENERATED KEYS - // AND USE THE KEY TO VALIDATE THE WRITE - batchWriter.submit(); + batch.add(entity4, entity5); + batch.add(PARTIAL_ENTITY1); + batch.put(ENTITY3, entity1, entity2); + + Batch.Response response = batch.submit(); Iterator entities = helper.fetch(KEY1, KEY2, KEY3, entity4.key(), entity5.key()).iterator(); assertEquals(entity1, entities.next()); @@ -260,19 +259,23 @@ public void testNewBatchWriter() { assertEquals(entity4, entities.next()); assertEquals(entity5, entities.next()); assertFalse(entities.hasNext()); + List generatedKeys = response.generatedKeys(); + assertEquals(1, generatedKeys.size()); + // todo(ozarov): uncomment after local datastore fix their bug with returned keys + // assertEquals(PARTIAL_ENTITY1.properties(), datastore.get(generatedKeys.get(0)).properties()); try { - batchWriter.submit(); + batch.submit(); fail("Expecting a failure"); } catch (DatastoreServiceException ex) { // expected to fail } - verifyNotUsable(batchWriter); + verifyNotUsable(batch); - batchWriter = datastore.newBatchWriter(); - batchWriter.delete(entity4.key(), entity5.key()); - batchWriter.update(ENTITY1, ENTITY2, ENTITY3); - batchWriter.submit(); + batch = datastore.newBatch(); + batch.delete(entity4.key(), entity5.key()); + batch.update(ENTITY1, ENTITY2, ENTITY3); + batch.submit(); entities = helper.fetch(KEY1, KEY2, KEY3, entity4.key(), entity5.key()).iterator(); assertEquals(ENTITY1, entities.next()); assertEquals(ENTITY2, entities.next()); From 86201c9c84fbc8885d6f657b9e3f86030da1f419 Mon Sep 17 00:00:00 2001 From: aozarov Date: Mon, 12 Jan 2015 16:14:48 -0800 Subject: [PATCH 090/771] Provide an option for an add with auto-id allocation. --- .../google/gcloud/datastore/BatchImpl.java | 4 +- .../google/gcloud/datastore/BlobValue.java | 42 ++++++++------- .../google/gcloud/datastore/BooleanValue.java | 42 ++++++++------- .../gcloud/datastore/DatastoreService.java | 2 +- .../datastore/DatastoreServiceImpl.java | 13 ++--- .../gcloud/datastore/DateTimeValue.java | 47 ++++++++-------- .../google/gcloud/datastore/DoubleValue.java | 42 ++++++++------- .../com/google/gcloud/datastore/Entity.java | 9 ++++ .../google/gcloud/datastore/EntityValue.java | 47 ++++++++-------- .../java/com/google/gcloud/datastore/Key.java | 18 +++++++ .../com/google/gcloud/datastore/KeyValue.java | 42 ++++++++------- .../google/gcloud/datastore/ListValue.java | 54 ++++++++++--------- .../google/gcloud/datastore/LongValue.java | 42 ++++++++------- .../google/gcloud/datastore/NullValue.java | 42 ++++++++------- .../gcloud/datastore/PartialEntity.java | 8 --- .../google/gcloud/datastore/PartialKey.java | 9 ---- .../com/google/gcloud/datastore/RawValue.java | 42 ++++++++------- .../google/gcloud/datastore/StringValue.java | 42 ++++++++------- .../gcloud/datastore/TransactionImpl.java | 12 ++--- .../com/google/gcloud/datastore/Value.java | 8 ++- .../google/gcloud/ExceptionHandlerTest.java | 4 +- .../com/google/gcloud/RetryHelperTest.java | 42 +++++++-------- .../DatastoreServiceIntegrationTest.java | 54 +++++++++---------- .../gcloud/datastore/LocalGcdHelper.java | 9 ++-- 24 files changed, 351 insertions(+), 325 deletions(-) diff --git a/src/main/java/com/google/gcloud/datastore/BatchImpl.java b/src/main/java/com/google/gcloud/datastore/BatchImpl.java index 57c07cc2741a..947f108aee7a 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchImpl.java +++ b/src/main/java/com/google/gcloud/datastore/BatchImpl.java @@ -23,11 +23,9 @@ class BatchImpl implements Batch { private final Set toDelete = new LinkedHashSet<>(); private final boolean force; final DatastoreServiceImpl datastore; - private boolean active = true; - - class ResponseImpl implements Response { + static class ResponseImpl implements Response { private final DatastoreV1.CommitResponse response; diff --git a/src/main/java/com/google/gcloud/datastore/BlobValue.java b/src/main/java/com/google/gcloud/datastore/BlobValue.java index 73671e167657..a495450a4967 100644 --- a/src/main/java/com/google/gcloud/datastore/BlobValue.java +++ b/src/main/java/com/google/gcloud/datastore/BlobValue.java @@ -11,26 +11,28 @@ public final class BlobValue extends Value { static final BaseMarshaller MARSHALLER = new BaseMarshaller() { - @Override - public int getProtoFieldId() { - return BLOB_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(Blob value) { - return builder(value); - } - - @Override - protected Blob getValue(DatastoreV1.Value from) { - return new Blob(from.getBlobValue(), false); - } - - @Override - protected void setValue(BlobValue from, DatastoreV1.Value.Builder to) { - to.setBlobValue(from.get().byteString()); - } - }; + private static final long serialVersionUID = -823515687083612387L; + + @Override + public int getProtoFieldId() { + return BLOB_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(Blob value) { + return builder(value); + } + + @Override + protected Blob getValue(DatastoreV1.Value from) { + return new Blob(from.getBlobValue(), false); + } + + @Override + protected void setValue(BlobValue from, DatastoreV1.Value.Builder to) { + to.setBlobValue(from.get().byteString()); + } + }; public static final class Builder extends Value.BaseBuilder { diff --git a/src/main/java/com/google/gcloud/datastore/BooleanValue.java b/src/main/java/com/google/gcloud/datastore/BooleanValue.java index 86e080b096fb..63de0e15791c 100644 --- a/src/main/java/com/google/gcloud/datastore/BooleanValue.java +++ b/src/main/java/com/google/gcloud/datastore/BooleanValue.java @@ -11,26 +11,28 @@ public final class BooleanValue extends Value { static final BaseMarshaller MARSHALLER = new BaseMarshaller() { - @Override - public int getProtoFieldId() { - return BOOLEAN_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(Boolean value) { - return builder(value); - } - - @Override - protected Boolean getValue(DatastoreV1.Value from) { - return from.getBooleanValue(); - } - - @Override - protected void setValue(BooleanValue from, DatastoreV1.Value.Builder to) { - to.setBooleanValue(from.get()); - } - }; + private static final long serialVersionUID = 7080467411349092522L; + + @Override + public int getProtoFieldId() { + return BOOLEAN_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(Boolean value) { + return builder(value); + } + + @Override + protected Boolean getValue(DatastoreV1.Value from) { + return from.getBooleanValue(); + } + + @Override + protected void setValue(BooleanValue from, DatastoreV1.Value.Builder to) { + to.setBooleanValue(from.get()); + } + }; public static final class Builder extends Value.BaseBuilder { diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index aba23f36a2d2..c4e71bcbc010 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -61,7 +61,7 @@ public interface DatastoreService extends DatastoreReaderWriter { * either newly allocated or the same one if was already complete * @throws DatastoreServiceException upon failure * @throws IllegalArgumentException if any of the given entities is missing a key - * @see #add(PartialKey) + * @see #add(PartialEntity) */ List add(PartialEntity... entity); diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index ae5b5619e62f..5070c21e613a 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -163,7 +163,7 @@ public List add(PartialEntity... entities) { if (entity instanceof Entity) { completeEntity = (Entity) entity; } else if (entity.key() instanceof Key) { - completeEntity = entity.toEntity((Key) entity.key()); + completeEntity = Entity.builder((Key) entity.key(), entity).build(); } if (completeEntity != null) { if (completeEntities.put(completeEntity.key(), completeEntity) != null) { @@ -186,7 +186,7 @@ public List add(PartialEntity... entities) { if (completeEntity != null) { responseBuilder.add(completeEntity); } else { - responseBuilder.add(entity.toEntity(Key.fromPb(allocatedKeys.next()))); + responseBuilder.add(Entity.builder(Key.fromPb(allocatedKeys.next()), entity).build()); } } return responseBuilder.build(); @@ -366,11 +366,12 @@ void rollbackTransaction(ByteString transaction) { rollback(requestPb.build()); } - DatastoreV1.RollbackResponse rollback(final DatastoreV1.RollbackRequest requestPb) { + void rollback(final DatastoreV1.RollbackRequest requestPb) { try { - return RetryHelper.runWithRetries(new Callable() { - @Override public DatastoreV1.RollbackResponse call() throws DatastoreException { - return datastore.rollback(requestPb); + RetryHelper.runWithRetries(new Callable() { + @Override public Void call() throws DatastoreException { + datastore.rollback(requestPb); + return null; } }, retryParams, EXCEPTION_HANDLER); } catch (RetryHelperException e) { diff --git a/src/main/java/com/google/gcloud/datastore/DateTimeValue.java b/src/main/java/com/google/gcloud/datastore/DateTimeValue.java index fff1b35d7f29..666dd24c2dfb 100644 --- a/src/main/java/com/google/gcloud/datastore/DateTimeValue.java +++ b/src/main/java/com/google/gcloud/datastore/DateTimeValue.java @@ -11,29 +11,30 @@ public final class DateTimeValue extends Value { static final BaseMarshaller MARSHALLER = new BaseMarshaller() { - @Override - public int getProtoFieldId() { - return TIMESTAMP_MICROSECONDS_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(DateTime value) { - return builder(value); - } - - @Override - protected DateTime getValue(DatastoreV1.Value from) { - return new DateTime(from.getTimestampMicrosecondsValue()); - } - - @Override - protected void setValue(DateTimeValue from, DatastoreV1.Value.Builder to) { - to.setTimestampMicrosecondsValue(from.get().timestampMicroseconds()); - } - }; - - public static final class Builder - extends Value.BaseBuilder { + private static final long serialVersionUID = -5695812592049332840L; + + @Override + public int getProtoFieldId() { + return TIMESTAMP_MICROSECONDS_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(DateTime value) { + return builder(value); + } + + @Override + protected DateTime getValue(DatastoreV1.Value from) { + return new DateTime(from.getTimestampMicrosecondsValue()); + } + + @Override + protected void setValue(DateTimeValue from, DatastoreV1.Value.Builder to) { + to.setTimestampMicrosecondsValue(from.get().timestampMicroseconds()); + } + }; + + public static final class Builder extends Value.BaseBuilder { private Builder() { super(Type.DATE_TIME); diff --git a/src/main/java/com/google/gcloud/datastore/DoubleValue.java b/src/main/java/com/google/gcloud/datastore/DoubleValue.java index 5d5b510739f0..b5c19f3545c6 100644 --- a/src/main/java/com/google/gcloud/datastore/DoubleValue.java +++ b/src/main/java/com/google/gcloud/datastore/DoubleValue.java @@ -11,26 +11,28 @@ public final class DoubleValue extends Value { static final BaseMarshaller MARSHALLER = new BaseMarshaller() { - @Override - public int getProtoFieldId() { - return DOUBLE_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(Double value) { - return builder(value); - } - - @Override - protected Double getValue(DatastoreV1.Value from) { - return from.getDoubleValue(); - } - - @Override - protected void setValue(DoubleValue from, DatastoreV1.Value.Builder to) { - to.setDoubleValue(from.get()); - } - }; + private static final long serialVersionUID = 3935522813529400538L; + + @Override + public int getProtoFieldId() { + return DOUBLE_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(Double value) { + return builder(value); + } + + @Override + protected Double getValue(DatastoreV1.Value from) { + return from.getDoubleValue(); + } + + @Override + protected void setValue(DoubleValue from, DatastoreV1.Value.Builder to) { + to.setDoubleValue(from.get()); + } + }; public static final class Builder extends Value.BaseBuilder { diff --git a/src/main/java/com/google/gcloud/datastore/Entity.java b/src/main/java/com/google/gcloud/datastore/Entity.java index 2d4ae4306942..292291792442 100644 --- a/src/main/java/com/google/gcloud/datastore/Entity.java +++ b/src/main/java/com/google/gcloud/datastore/Entity.java @@ -33,6 +33,11 @@ private Builder(Entity entity) { key = entity.key(); } + private Builder(Key key, BaseEntity entity) { + super(entity); + this.key = key; + } + public Builder key(Key key) { this.key = checkNotNull(key); return this; @@ -74,4 +79,8 @@ public static Builder builder(Key key) { public static Builder builder(Entity copyFrom) { return new Builder(copyFrom); } + + public static Builder builder(Key key, PartialEntity copyFrom) { + return new Builder(key, copyFrom); + } } diff --git a/src/main/java/com/google/gcloud/datastore/EntityValue.java b/src/main/java/com/google/gcloud/datastore/EntityValue.java index 061cf6c57fa5..c4e46bebc8ae 100644 --- a/src/main/java/com/google/gcloud/datastore/EntityValue.java +++ b/src/main/java/com/google/gcloud/datastore/EntityValue.java @@ -12,29 +12,30 @@ public class EntityValue extends Value { static final BaseMarshaller MARSHALLER = new BaseMarshaller() { - @Override - public int getProtoFieldId() { - return ENTITY_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(PartialEntity value) { - return builder(value); - } - - @Override - protected PartialEntity getValue(DatastoreV1.Value from) { - return PartialEntity.fromPb(from.getEntityValue()); - } - - @Override - protected void setValue(EntityValue from, DatastoreV1.Value.Builder to) { - to.setEntityValue(from.get().toPb()); - } - }; - - public static final class Builder extends - Value.BaseBuilder { + private static final long serialVersionUID = 2355075086076070931L; + + @Override + public int getProtoFieldId() { + return ENTITY_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(PartialEntity value) { + return builder(value); + } + + @Override + protected PartialEntity getValue(DatastoreV1.Value from) { + return PartialEntity.fromPb(from.getEntityValue()); + } + + @Override + protected void setValue(EntityValue from, DatastoreV1.Value.Builder to) { + to.setEntityValue(from.get().toPb()); + } + }; + + public static final class Builder extends Value.BaseBuilder { private Builder() { super(Type.ENTITY); diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index c35efaeb6849..43ea51d83c1c 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -38,6 +38,16 @@ private Builder(String dataset, String kind, long id) { this.id = id; } + private Builder(PartialKey copyFrom, String name) { + super(copyFrom); + this.name = name; + } + + private Builder(PartialKey copyFrom, long id) { + super(copyFrom); + this.id = id; + } + private Builder(Key copyFrom) { super(copyFrom); if (copyFrom.hasId()) { @@ -158,6 +168,14 @@ public static Builder builder(Key copyFrom) { return new Builder(copyFrom); } + public static Builder builder(PartialKey copyFrom, String name) { + return new Builder(copyFrom, name); + } + + public static Builder builder(PartialKey copyFrom, long id) { + return new Builder(copyFrom, id); + } + public static Builder builder(Key parent, String kind, String name) { Builder builder = builder(parent.dataset(), kind, name); addParentToBuilder(parent, builder); diff --git a/src/main/java/com/google/gcloud/datastore/KeyValue.java b/src/main/java/com/google/gcloud/datastore/KeyValue.java index a97399b5006c..65163103ede4 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyValue.java +++ b/src/main/java/com/google/gcloud/datastore/KeyValue.java @@ -11,26 +11,28 @@ public final class KeyValue extends Value { static final BaseMarshaller MARSHALLER = new BaseMarshaller() { - @Override - public int getProtoFieldId() { - return KEY_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(Key key) { - return builder(key); - } - - @Override - protected Key getValue(DatastoreV1.Value from) { - return Key.fromPb(from.getKeyValue()); - } - - @Override - protected void setValue(KeyValue from, DatastoreV1.Value.Builder to) { - to.setKeyValue(from.get().toPb()); - } - }; + private static final long serialVersionUID = 5449133205064700403L; + + @Override + public int getProtoFieldId() { + return KEY_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(Key key) { + return builder(key); + } + + @Override + protected Key getValue(DatastoreV1.Value from) { + return Key.fromPb(from.getKeyValue()); + } + + @Override + protected void setValue(KeyValue from, DatastoreV1.Value.Builder to) { + to.setKeyValue(from.get().toPb()); + } + }; public static final class Builder extends Value.BaseBuilder { diff --git a/src/main/java/com/google/gcloud/datastore/ListValue.java b/src/main/java/com/google/gcloud/datastore/ListValue.java index 5b932c8c1da1..4132858432b4 100644 --- a/src/main/java/com/google/gcloud/datastore/ListValue.java +++ b/src/main/java/com/google/gcloud/datastore/ListValue.java @@ -16,32 +16,34 @@ public final class ListValue extends Value>> { static final BaseMarshaller>, ListValue, Builder> MARSHALLER = new BaseMarshaller>, ListValue, Builder>() { - @Override - public int getProtoFieldId() { - return LIST_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(List> values) { - return builder().set(values); - } - - @Override - protected List> getValue(DatastoreV1.Value from) { - List> properties = new ArrayList<>(from.getListValueCount()); - for (DatastoreV1.Value valuePb : from.getListValueList()) { - properties.add(Value.fromPb(valuePb)); - } - return properties; - } - - @Override - protected void setValue(ListValue from, DatastoreV1.Value.Builder to) { - for (Value property : from.get()) { - to.addListValue(property.toPb()); - } - } - }; + private static final long serialVersionUID = -3193794036327640106L; + + @Override + public int getProtoFieldId() { + return LIST_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(List> values) { + return builder().set(values); + } + + @Override + protected List> getValue(DatastoreV1.Value from) { + List> properties = new ArrayList<>(from.getListValueCount()); + for (DatastoreV1.Value valuePb : from.getListValueList()) { + properties.add(Value.fromPb(valuePb)); + } + return properties; + } + + @Override + protected void setValue(ListValue from, DatastoreV1.Value.Builder to) { + for (Value property : from.get()) { + to.addListValue(property.toPb()); + } + } + }; public static final class Builder extends Value.BaseBuilder>, ListValue, Builder> { diff --git a/src/main/java/com/google/gcloud/datastore/LongValue.java b/src/main/java/com/google/gcloud/datastore/LongValue.java index 4ab977ed96da..ac887e4c7263 100644 --- a/src/main/java/com/google/gcloud/datastore/LongValue.java +++ b/src/main/java/com/google/gcloud/datastore/LongValue.java @@ -11,26 +11,28 @@ public final class LongValue extends Value { static final BaseMarshaller MARSHALLER = new BaseMarshaller() { - @Override - public int getProtoFieldId() { - return INTEGER_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(Long value) { - return builder(value); - } - - @Override - protected Long getValue(DatastoreV1.Value from) { - return from.getIntegerValue(); - } - - @Override - protected void setValue(LongValue from, DatastoreV1.Value.Builder to) { - to.setIntegerValue(from.get()); - } - }; + private static final long serialVersionUID = 2137414214660959845L; + + @Override + public int getProtoFieldId() { + return INTEGER_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(Long value) { + return builder(value); + } + + @Override + protected Long getValue(DatastoreV1.Value from) { + return from.getIntegerValue(); + } + + @Override + protected void setValue(LongValue from, DatastoreV1.Value.Builder to) { + to.setIntegerValue(from.get()); + } + }; public static final class Builder extends Value.BaseBuilder { diff --git a/src/main/java/com/google/gcloud/datastore/NullValue.java b/src/main/java/com/google/gcloud/datastore/NullValue.java index af81c017bc72..58c91e8342cb 100644 --- a/src/main/java/com/google/gcloud/datastore/NullValue.java +++ b/src/main/java/com/google/gcloud/datastore/NullValue.java @@ -11,26 +11,28 @@ public final class NullValue extends Value { static final BaseMarshaller MARSHALLER = new BaseMarshaller() { - @Override - public Builder newBuilder(Void value) { - return builder(); - } - - @Override - public int getProtoFieldId() { - return 0; - } - - @Override - protected Void getValue(DatastoreV1.Value from) { - return null; - } - - @Override - protected void setValue(NullValue from, DatastoreV1.Value.Builder to) { - // nothing to set - } - }; + private static final long serialVersionUID = 2785573597627128832L; + + @Override + public Builder newBuilder(Void value) { + return builder(); + } + + @Override + public int getProtoFieldId() { + return 0; + } + + @Override + protected Void getValue(DatastoreV1.Value from) { + return null; + } + + @Override + protected void setValue(NullValue from, DatastoreV1.Value.Builder to) { + // nothing to set + } + }; public static final class Builder extends Value.BaseBuilder { diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java index c1dead077d57..b774e30c2a3d 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java @@ -47,14 +47,6 @@ protected PartialEntity(PartialKey key, ImmutableSortedMap> pro this.key = key; } - /** - * Returns a new {@link Entity} with the same properties as this one and - * with the given {@code key}. - */ - public Entity toEntity(Key key) { - return new Entity(key, ImmutableSortedMap.copyOf(properties())); - } - public boolean hasKey() { return key != null; } diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index fa28ed906e2f..74659534b3c1 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -38,15 +38,6 @@ public PartialKey build() { super(dataset, namespace, path); } - public Key newKey(String name) { - return Key.builder(dataset(), kind(), name) - .namespace(namespace()).ancestors(ancestors()).build(); - } - - public Key newKey(long id) { - return Key.builder(dataset(), kind(), id).namespace(namespace()).ancestors(ancestors()).build(); - } - @Override protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { return fromPb(DatastoreV1.Key.parseFrom(bytesPb)); diff --git a/src/main/java/com/google/gcloud/datastore/RawValue.java b/src/main/java/com/google/gcloud/datastore/RawValue.java index d5fc455df4a9..17e5b70b12ab 100644 --- a/src/main/java/com/google/gcloud/datastore/RawValue.java +++ b/src/main/java/com/google/gcloud/datastore/RawValue.java @@ -9,26 +9,28 @@ public final class RawValue extends Value { static final BaseMarshaller MARSHALLER = new BaseMarshaller() { - @Override - public Builder newBuilder(DatastoreV1.Value value) { - return builder(value); - } - - @Override - public int getProtoFieldId() { - return 0; - } - - @Override - protected DatastoreV1.Value getValue(DatastoreV1.Value from) { - return from; - } - - @Override - protected void setValue(RawValue from, DatastoreV1.Value.Builder to) { - to.mergeFrom(from.get()); - } - }; + private static final long serialVersionUID = 5320642719486106244L; + + @Override + public Builder newBuilder(DatastoreV1.Value value) { + return builder(value); + } + + @Override + public int getProtoFieldId() { + return 0; + } + + @Override + protected DatastoreV1.Value getValue(DatastoreV1.Value from) { + return from; + } + + @Override + protected void setValue(RawValue from, DatastoreV1.Value.Builder to) { + to.mergeFrom(from.get()); + } + }; static final class Builder extends Value.BaseBuilder { diff --git a/src/main/java/com/google/gcloud/datastore/StringValue.java b/src/main/java/com/google/gcloud/datastore/StringValue.java index dad18654c8bd..ae09e39b6b6e 100644 --- a/src/main/java/com/google/gcloud/datastore/StringValue.java +++ b/src/main/java/com/google/gcloud/datastore/StringValue.java @@ -12,26 +12,28 @@ public final class StringValue extends Value { static final BaseMarshaller MARSHALLER = new BaseMarshaller() { - @Override - public int getProtoFieldId() { - return STRING_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(String value) { - return builder(value); - } - - @Override - protected String getValue(DatastoreV1.Value from) { - return from.getStringValue(); - } - - @Override - protected void setValue(StringValue from, DatastoreV1.Value.Builder to) { - to.setStringValue(from.get()); - } - }; + private static final long serialVersionUID = -359610204134164436L; + + @Override + public int getProtoFieldId() { + return STRING_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(String value) { + return builder(value); + } + + @Override + protected String getValue(DatastoreV1.Value from) { + return from.getStringValue(); + } + + @Override + protected void setValue(StringValue from, DatastoreV1.Value.Builder to) { + to.setStringValue(from.get()); + } + }; public static final class Builder extends Value.BaseBuilder { diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java index c2b4b3c9d5ae..1b33ae19b4d8 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java @@ -15,9 +15,9 @@ final class TransactionImpl extends BatchImpl implements Transaction { private final ByteString transaction; - private boolean wasRolledback; + private boolean rolledback; - class ResponseImpl extends BatchImpl.ResponseImpl implements Transaction.Response { + static class ResponseImpl extends BatchImpl.ResponseImpl implements Transaction.Response { public ResponseImpl(DatastoreV1.CommitResponse response) { super(response); @@ -77,15 +77,15 @@ public Transaction.Response commit() { @Override public void rollback() { super.validateActive(); - if (!wasRolledback) { + if (!rolledback) { datastore.rollbackTransaction(transaction); } - wasRolledback = true; + rolledback = true; } @Override public boolean active() { - return super.active() && !wasRolledback; + return super.active() && !rolledback; } @Override @@ -96,7 +96,7 @@ protected String getName() { @Override protected void validateActive() { super.validateActive(); - if (wasRolledback) { + if (rolledback) { throw throwInvalidRequest(getName() + " is not active (was rolledback)"); } } diff --git a/src/main/java/com/google/gcloud/datastore/Value.java b/src/main/java/com/google/gcloud/datastore/Value.java index c45e8bdaa0fd..ff064e87178e 100644 --- a/src/main/java/com/google/gcloud/datastore/Value.java +++ b/src/main/java/com/google/gcloud/datastore/Value.java @@ -129,11 +129,13 @@ interface Builder, B extends Builder> { P build(); } - interface BuilderFactory, B extends Builder> { + interface BuilderFactory, B extends Builder> + extends java.io.Serializable { B newBuilder(V value); } - interface Marshaller, B extends Builder> { + interface Marshaller, B extends Builder> + extends java.io.Serializable { B fromProto(DatastoreV1.Value proto); @@ -145,6 +147,8 @@ interface Marshaller, B extends Builder> { abstract static class BaseMarshaller, B extends Builder> implements Marshaller, BuilderFactory { + private static final long serialVersionUID = 2880767488942992985L; + @Override public final B fromProto(DatastoreV1.Value proto) { B builder = newBuilder(getValue(proto)); diff --git a/src/test/java/com/google/gcloud/ExceptionHandlerTest.java b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java index 57706ece50d1..960f293e0baa 100644 --- a/src/test/java/com/google/gcloud/ExceptionHandlerTest.java +++ b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java @@ -113,9 +113,7 @@ public void testShouldTry() { assertTrue(handler.shouldRetry(new NullPointerException())); final AtomicReference before = new AtomicReference<>(RetryResult.ABORT); - Interceptor interceptor = new Interceptor() { - - private static final long serialVersionUID = 1; + @SuppressWarnings("serial") Interceptor interceptor = new Interceptor() { @Override public RetryResult afterEval(Exception exception, RetryResult retryResult) { diff --git a/src/test/java/com/google/gcloud/RetryHelperTest.java b/src/test/java/com/google/gcloud/RetryHelperTest.java index 7f89d5e654a6..b8b0f7a4c501 100644 --- a/src/test/java/com/google/gcloud/RetryHelperTest.java +++ b/src/test/java/com/google/gcloud/RetryHelperTest.java @@ -42,6 +42,22 @@ */ public class RetryHelperTest { + static class E1Exception extends Exception { + private static final long serialVersionUID = 3874933713392137001L; + } + + static class E2Exception extends E1Exception { + private static final long serialVersionUID = -8710227162480133598L; + } + + static class E3Exception extends E1Exception { + private static final long serialVersionUID = -7794256022024001666L; + } + + static class E4Exception extends E2Exception { + private static final long serialVersionUID = -5508018234693709156L; + } + @Test public void testTriesWithExceptionHandling() { assertNull(RetryHelper.getContext()); @@ -68,22 +84,6 @@ public void testTriesWithExceptionHandling() { } assertNull(RetryHelper.getContext()); - class E1Exception extends Exception { - private static final long serialVersionUID = 3874933713392137001L; - } - - class E2Exception extends E1Exception { - private static final long serialVersionUID = -8710227162480133598L; - } - - class E3Exception extends E1Exception { - private static final long serialVersionUID = -7794256022024001666L; - } - - class E4Exception extends E2Exception { - private static final long serialVersionUID = -5508018234693709156L; - } - params = RetryParams.builder().initialRetryDelayMillis(0).retryMaxAttempts(5).build(); handler = ExceptionHandler.builder() .retryOn(E1Exception.class, E4Exception.class) @@ -161,14 +161,8 @@ private static class FakeTicker extends Ticker { private final AtomicLong nanos = new AtomicLong(); // Advances the ticker value by {@code time} in {@code timeUnit}. - FakeTicker advance(long time, TimeUnit timeUnit) { - return advance(timeUnit.toNanos(time)); - } - - // Advances the ticker value by {@code nanoseconds}. - FakeTicker advance(long nanoseconds) { - nanos.addAndGet(nanoseconds); - return this; + void advance(long time, TimeUnit timeUnit) { + nanos.addAndGet(timeUnit.toNanos(time)); } @Override diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java index ec40a29535b8..3d2d751e159b 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java @@ -33,16 +33,17 @@ public class DatastoreServiceIntegrationTest { private static final String DATASET = LocalGcdHelper.DEFAULT_DATASET; private static final String KIND1 = "kind1"; private static final String KIND2 = "kind2"; + private static final String KIND3 = "kind3"; private static final NullValue NULL_VALUE = NullValue.of(); private static final StringValue STR_VALUE = StringValue.of("str"); private static final BooleanValue BOOL_VALUE = BooleanValue.builder(false).indexed(false).build(); private static final PartialKey PARTIAL_KEY1 = PartialKey.builder(DATASET, KIND1).build(); private static final PartialKey PARTIAL_KEY2 = PartialKey.builder(DATASET, KIND2).build(); - private static final Key KEY1 = PARTIAL_KEY1.newKey("name"); + private static final Key KEY1 = Key.builder(PARTIAL_KEY1, "name").build(); private static final Key KEY2 = Key.builder(KEY1, KIND2, 1).build(); private static final Key KEY3 = Key.builder(KEY2).name("bla").build(); - private static final Key KEY4 = KEY2.newKey("newName1"); - private static final Key KEY5 = KEY2.newKey("newName2"); + private static final Key KEY4 = Key.builder(KEY2).name("newName1").build(); + private static final Key KEY5 = Key.builder(KEY2).name("newName2").build(); private static final KeyValue KEY_VALUE = KeyValue.of(KEY1); private static final ListValue LIST_VALUE1 = ListValue.builder() .addValue(NULL_VALUE) @@ -54,6 +55,8 @@ public class DatastoreServiceIntegrationTest { .set("str", STR_VALUE).set("bool", BOOL_VALUE).set("list", LIST_VALUE1).build(); private static final PartialEntity PARTIAL_ENTITY2 = PartialEntity.builder(PARTIAL_ENTITY1) .remove("str").set("bool", true).set("list", LIST_VALUE1.get()).build(); + private static final PartialEntity PARTIAL_ENTITY3 = PartialEntity.builder(PARTIAL_ENTITY1) + .key(PartialKey.builder(DATASET, KIND3).build()).build(); private static final Entity ENTITY1 = Entity.builder(KEY1) .set("str", STR_VALUE) .set("date", DATE_TIME_VALUE) @@ -247,7 +250,7 @@ public void testNewBatch() { Entity entity5 = Entity.builder(KEY5).set("value", "value").build(); batch.add(entity4, entity5); - batch.add(PARTIAL_ENTITY1); + batch.add(PARTIAL_ENTITY3); batch.put(ENTITY3, entity1, entity2); Batch.Response response = batch.submit(); @@ -459,15 +462,15 @@ public void testAllocateId() { assertEquals(key1.kind(), pk1.kind()); assertTrue(key1.hasId()); assertFalse(key1.hasName()); - assertEquals(pk1.newKey(key1.id()), key1); + assertEquals(Key.builder(pk1, key1.id()).build(), key1); Key key2 = datastore.allocateId(pk1); assertNotEquals(key1, key2); - assertEquals(pk1.newKey(key2.id()), key2); + assertEquals(Key.builder(pk1, key2.id()).build(), key2); Key key3 = datastore.allocateId(key1); assertNotEquals(key1, key3); - assertEquals(pk1.newKey(key3.id()), key3); + assertEquals(Key.builder(pk1, key3.id()).build(), key3); } @Test @@ -480,9 +483,9 @@ public void testAllocateIdArray() { List result = datastore.allocateId(partialKey1, partialKey2, key3, key4, partialKey1, key3); assertEquals(6, result.size()); - assertEquals(partialKey1.newKey(result.get(0).id()), result.get(0)); - assertEquals(partialKey1.newKey(result.get(4).id()), result.get(4)); - assertEquals(partialKey2.newKey(result.get(1).id()), result.get(1)); + assertEquals(Key.builder(partialKey1, result.get(0).id()).build(), result.get(0)); + assertEquals(Key.builder(partialKey1, result.get(4).id()).build(), result.get(4)); + assertEquals(Key.builder(partialKey2, result.get(1).id()).build(), result.get(1)); assertEquals(Key.builder(key3).id(result.get(2).id()).build(), result.get(2)); assertEquals(Key.builder(key3).id(result.get(5).id()).build(), result.get(5)); assertEquals(Key.builder(key4).id(result.get(3).id()).build(), result.get(3)); @@ -571,23 +574,20 @@ public void testAddPartialEntity() { } PartialEntity pe = PartialEntity.builder(PARTIAL_ENTITY2).key(KEY5).build(); - List response = - datastore.add(PARTIAL_ENTITY1, PARTIAL_ENTITY2, ENTITY3, PARTIAL_ENTITY1, pe); - assertEquals(5, response.size()); - assertEquals(PARTIAL_ENTITY1.properties(), response.get(0).properties()); - assertEquals(PARTIAL_ENTITY1.properties(), datastore.get(response.get(0).key()).properties()); - assertEquals(PARTIAL_ENTITY2.properties(), response.get(1).properties()); - assertEquals(PARTIAL_ENTITY2.properties(), datastore.get(response.get(1).key()).properties()); - assertSame(ENTITY3, response.get(2)); - assertEquals(ENTITY3, datastore.get(response.get(2).key())); - assertEquals(PARTIAL_ENTITY1.properties(), response.get(3).properties()); - assertEquals(PARTIAL_ENTITY1.properties(), datastore.get(response.get(3).key()).properties()); - assertEquals(pe.properties(), response.get(4).properties()); - assertEquals(pe.key(), response.get(4).key()); - assertEquals(pe.properties(), datastore.get(response.get(4).key()).properties()); - assertEquals(pe.key(), datastore.get(response.get(4).key()).key()); - assertEquals(pe, response.get(4)); - assertEquals(datastore.get(response.get(4).key()), response.get(4)); + List response = datastore.add(PARTIAL_ENTITY3, ENTITY3, PARTIAL_ENTITY3, pe); + assertEquals(4, response.size()); + assertEquals(PARTIAL_ENTITY3.properties(), response.get(0).properties()); + assertEquals(PARTIAL_ENTITY3.properties(), datastore.get(response.get(0).key()).properties()); + assertSame(ENTITY3, response.get(1)); + assertEquals(ENTITY3, datastore.get(response.get(1).key())); + assertEquals(PARTIAL_ENTITY3.properties(), response.get(2).properties()); + assertEquals(PARTIAL_ENTITY3.properties(), datastore.get(response.get(2).key()).properties()); + assertEquals(pe.properties(), response.get(3).properties()); + assertEquals(pe.key(), response.get(3).key()); + assertEquals(pe.properties(), datastore.get(response.get(3).key()).properties()); + assertEquals(pe.key(), datastore.get(response.get(3).key()).key()); + assertEquals(pe, response.get(3)); + assertEquals(datastore.get(response.get(3).key()), response.get(3)); } @Test diff --git a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java index 4738158eb319..64fe814e5140 100644 --- a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java +++ b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java @@ -106,7 +106,7 @@ public void start() throws IOException, InterruptedException { } File datasetFolder = new File(gcdFolder, GCD + '/' + dataset); - datasetFolder.delete(); + deleteRecurse(datasetFolder.toPath()); // TODO: if System.getProperty("os.name").startsWith("Windows") use cmd.exe /c and gcd.cmd Process temp = new ProcessBuilder() @@ -209,7 +209,7 @@ public static void main(String... args) throws IOException, InterruptedException String path = reader.readLine(); deleteRecurse(Paths.get(path)); } - file.delete(); + file.delete(); } return; default: @@ -221,8 +221,9 @@ public static void main(String... args) throws IOException, InterruptedException public static boolean isActive(String dataset) { try { - String path = "/datastore/v1beta2/datasets/" + dataset + "/lookup"; - URL url = new URL("http://localhost:" + PORT + path); + StringBuilder urlBuilder = new StringBuilder("http://localhost:").append(PORT); + urlBuilder.append("/datastore/v1beta2/datasets/").append(dataset).append("/lookup"); + URL url = new URL(urlBuilder.toString()); try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) { return "Valid RPC".equals(reader.readLine()); From fdac9a74cde9627cfd508a8a0f85e16e3d4c83ff Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 14 Jan 2015 10:46:44 -0800 Subject: [PATCH 091/771] replace local gcd hack with a fixed version of gcd --- .../com/google/gcloud/storage/Bucket.java | 8 +- .../java/com/google/gcloud/storage/Cors.java | 122 ++++++++++++++++++ .../gcloud/datastore/LocalGcdHelper.java | 2 +- 3 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/google/gcloud/storage/Cors.java diff --git a/src/main/java/com/google/gcloud/storage/Bucket.java b/src/main/java/com/google/gcloud/storage/Bucket.java index 2149cafca6f6..be2653494fd9 100644 --- a/src/main/java/com/google/gcloud/storage/Bucket.java +++ b/src/main/java/com/google/gcloud/storage/Bucket.java @@ -4,15 +4,19 @@ public interface Bucket { + String id(); + String name(); Acl acl(); - void updateAcl(Acl acl); - Acl defaultObjectAcl(); + Cors cors(); + void updateDefaultObjectAcl(); + void updateAcl(Acl acl); + diff --git a/src/main/java/com/google/gcloud/storage/Cors.java b/src/main/java/com/google/gcloud/storage/Cors.java new file mode 100644 index 000000000000..71e87da50fd3 --- /dev/null +++ b/src/main/java/com/google/gcloud/storage/Cors.java @@ -0,0 +1,122 @@ +/* + * 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.storage; + +import com.google.common.collect.ImmutableList; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +public class Cors { + + private final Integer maxAgeSeconds; + private final ImmutableList methods; + private final ImmutableList origins; + private final ImmutableList responseHeaders; + + public enum Method { + ANY, GET, HEAD, PUT, POST, DELETE + } + + public static class Origin { + + private final URI uri; + + public static final Origin ANY = new Origin(); + + private Origin() { + uri = null; + } + + public Origin(String scheme, String host, int port) { + try { + this.uri = new URI(scheme, null, host, port, null, null, null); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + + } + + @Override + public String toString() { + return uri == null ? "*" : uri.toString(); + } + } + + public static final class Builder { + + private Integer maxAgeSeconds; + private ImmutableList methods; + private ImmutableList origins; + private ImmutableList responseHeaders; + + private Builder() { + } + + public Builder maxAgeSeconds(Integer maxAgeSeconds) { + this.maxAgeSeconds = maxAgeSeconds; + return this; + } + + public Builder methods(Method... methods) { + this.methods = ImmutableList.copyOf(methods); + return this; + } + + public Builder origins(Origin... origins) { + this.origins = ImmutableList.copyOf(origins); + return this; + } + + public Builder responseHeaders(String... responseHeaders) { + this.responseHeaders = ImmutableList.copyOf(responseHeaders); + return this; + } + + public Cors build() { + return new Cors(this); + } + } + + private Cors(Builder builder) { + this.maxAgeSeconds = builder.maxAgeSeconds; + this.methods = builder.methods; + this.origins = builder.origins; + this.responseHeaders = builder.responseHeaders; + } + + public Integer maxAgeSeconds() { + return maxAgeSeconds; + } + + public List methods() { + return methods; + } + + public List origins() { + return origins; + } + + public List responseHeaders() { + return responseHeaders; + } + + public static Builder builder() { + return new Builder(); + } +} diff --git a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java index 64fe814e5140..279cb0b2392e 100644 --- a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java +++ b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java @@ -36,7 +36,7 @@ public class LocalGcdHelper { public static final String DEFAULT_DATASET = "dataset1"; public static final int PORT = 8080; - private static final String GCD = "gcd-v1beta2-rev1-2.1.1"; + private static final String GCD = "gcd-head"; private static final String GCD_LOC = '/' + GCD + ".zip"; private static class ProcessStreamReader extends Thread { From f7d44d5d35bd120392512767e974eb0715254377 Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 14 Jan 2015 10:47:23 -0800 Subject: [PATCH 092/771] replace local gcd hack with a fixed version of gcd --- .../DatastoreServiceIntegrationTest.java | 49 +------------------ 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java index 3d2d751e159b..4af76c5ca07e 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java @@ -8,14 +8,12 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.google.api.services.datastore.DatastoreV1; -import com.google.api.services.datastore.client.Datastore; import com.google.api.services.datastore.client.DatastoreException; import com.google.gcloud.datastore.Query.Type; import com.google.gcloud.datastore.StructuredQuery.OrderBy; import com.google.gcloud.datastore.StructuredQuery.Projection; import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; -import org.easymock.EasyMock; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -264,8 +262,7 @@ public void testNewBatch() { assertFalse(entities.hasNext()); List generatedKeys = response.generatedKeys(); assertEquals(1, generatedKeys.size()); - // todo(ozarov): uncomment after local datastore fix their bug with returned keys - // assertEquals(PARTIAL_ENTITY1.properties(), datastore.get(generatedKeys.get(0)).properties()); + assertEquals(PARTIAL_ENTITY1.properties(), datastore.get(generatedKeys.get(0)).properties()); try { batch.submit(); @@ -335,26 +332,6 @@ public void testRunGqlQueryNoCasting() throws DatastoreException { GqlQuery projectionQuery = GqlQuery.builder( Type.PROJECTION, "select str, date from " + KIND1).build(); - // this hack is needed because of b/18806697 - DatastoreV1.RunQueryRequest.Builder requestPb = DatastoreV1.RunQueryRequest.newBuilder(); - requestPb.setGqlQuery(projectionQuery.toPb()); - requestPb.setPartitionId(DatastoreV1.PartitionId.newBuilder().setDatasetId(DATASET).build()); - DatastoreV1.RunQueryResponse responsePb = - ((DatastoreServiceImpl) datastore).runQuery(requestPb.build()); - DatastoreV1.RunQueryResponse.Builder responsePbBuilder = responsePb.toBuilder(); - responsePbBuilder.getBatchBuilder() - .setEntityResultType(DatastoreV1.EntityResult.ResultType.PROJECTION).build(); - Datastore mockDatastore = EasyMock.createMock(Datastore.class); - DatastoreV1.EntityResult found = - DatastoreV1.EntityResult.newBuilder().setEntity(ENTITY1.toPb()).build(); - EasyMock.expect(mockDatastore.lookup(EasyMock.anyObject())) - .andReturn(DatastoreV1.LookupResponse.newBuilder().addFound(found).build()); - EasyMock.expect(mockDatastore.runQuery(requestPb.build())).andReturn(responsePbBuilder.build()); - EasyMock.replay(mockDatastore); - datastore = DatastoreServiceFactory.getDefault( - datastore.options().toBuilder().datastore(mockDatastore).build()); - // end of hack - QueryResult projectionResult = datastore.run(projectionQuery); assertTrue(projectionResult.hasNext()); projectionEntity = projectionResult.next(); @@ -364,7 +341,6 @@ public void testRunGqlQueryNoCasting() throws DatastoreException { projectionEntity.getLong("date")); assertEquals(2, projectionEntity.names().size()); assertFalse(projectionResult.hasNext()); - EasyMock.verify(mockDatastore); } @Test @@ -418,25 +394,6 @@ public void testRunStructuredQuery() throws DatastoreException { .orderBy(OrderBy.asc("age")) .limit(10) .build(); - // this hack is needed because of b/18806697 - DatastoreV1.RunQueryRequest.Builder requestPb = DatastoreV1.RunQueryRequest.newBuilder(); - requestPb.setQuery(projectionQuery.toPb()); - requestPb.setPartitionId(DatastoreV1.PartitionId.newBuilder().setDatasetId(DATASET).build()); - DatastoreV1.RunQueryResponse responsePb = - ((DatastoreServiceImpl) datastore).runQuery(requestPb.build()); - DatastoreV1.RunQueryResponse.Builder responsePbBuilder = responsePb.toBuilder(); - responsePbBuilder.getBatchBuilder() - .setEntityResultType(DatastoreV1.EntityResult.ResultType.PROJECTION).build(); - Datastore mockDatastore = EasyMock.createMock(Datastore.class); - DatastoreV1.EntityResult missing = - DatastoreV1.EntityResult.newBuilder().setEntity(ENTITY1.toPb()).build(); - EasyMock.expect(mockDatastore.lookup(EasyMock.anyObject())) - .andReturn(DatastoreV1.LookupResponse.newBuilder().addMissing(missing).build()); - EasyMock.expect(mockDatastore.runQuery(requestPb.build())).andReturn(responsePbBuilder.build()); - EasyMock.replay(mockDatastore); - datastore = DatastoreServiceFactory.getDefault( - datastore.options().toBuilder().datastore(mockDatastore).build()); - // end of hack QueryResult results4 = datastore.run(projectionQuery); assertTrue(results4.hasNext()); @@ -446,8 +403,6 @@ public void testRunStructuredQuery() throws DatastoreException { assertEquals("Dan", entity.getString("name")); assertEquals(2, entity.properties().size()); assertFalse(results4.hasNext()); - EasyMock.verify(mockDatastore); - // TODO(ozarov): construct a test to verify nextQuery/pagination } From 01e0ff7c9ff88390432fdfbb376cc9c22c5e53cb Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 20 Jan 2015 15:23:21 -0800 Subject: [PATCH 093/771] Adding copyright and README and CONTRIBUTIONS files. --- .gitignore | 33 +--------- README.md | 65 ++++++++++++++++++- .../java/com/google/gcloud/AuthConfig.java | 16 +++++ .../com/google/gcloud/ExceptionHandler.java | 16 +++++ .../java/com/google/gcloud/RetryHelper.java | 16 +++++ .../java/com/google/gcloud/RetryParams.java | 16 +++++ .../com/google/gcloud/ServiceOptions.java | 16 +++++ .../google/gcloud/datastore/BaseEntity.java | 16 +++++ .../com/google/gcloud/datastore/BaseKey.java | 16 +++++ .../com/google/gcloud/datastore/Batch.java | 16 +++++ .../google/gcloud/datastore/BatchImpl.java | 16 +++++ .../google/gcloud/datastore/BatchOption.java | 16 +++++ .../com/google/gcloud/datastore/Blob.java | 16 +++++ .../google/gcloud/datastore/BlobValue.java | 16 +++++ .../google/gcloud/datastore/BooleanValue.java | 16 +++++ .../com/google/gcloud/datastore/Cursor.java | 16 +++++ .../gcloud/datastore/DatastoreHelper.java | 16 +++++ .../gcloud/datastore/DatastoreReader.java | 16 +++++ .../datastore/DatastoreReaderWriter.java | 16 +++++ .../gcloud/datastore/DatastoreService.java | 16 +++++ .../datastore/DatastoreServiceException.java | 16 +++++ .../datastore/DatastoreServiceFactory.java | 16 +++++ .../datastore/DatastoreServiceImpl.java | 16 +++++ .../datastore/DatastoreServiceOptions.java | 16 +++++ .../gcloud/datastore/DatastoreWriter.java | 16 +++++ .../com/google/gcloud/datastore/DateTime.java | 16 +++++ .../gcloud/datastore/DateTimeValue.java | 16 +++++ .../google/gcloud/datastore/DoubleValue.java | 16 +++++ .../com/google/gcloud/datastore/Entity.java | 16 +++++ .../google/gcloud/datastore/EntityValue.java | 16 +++++ .../com/google/gcloud/datastore/GqlQuery.java | 16 +++++ .../java/com/google/gcloud/datastore/Key.java | 16 +++++ .../google/gcloud/datastore/KeyFactory.java | 16 +++++ .../com/google/gcloud/datastore/KeyValue.java | 16 +++++ .../google/gcloud/datastore/ListValue.java | 16 +++++ .../google/gcloud/datastore/LongValue.java | 16 +++++ .../google/gcloud/datastore/NullValue.java | 16 +++++ .../gcloud/datastore/PartialEntity.java | 16 +++++ .../google/gcloud/datastore/PartialKey.java | 16 +++++ .../google/gcloud/datastore/PathElement.java | 16 +++++ .../gcloud/datastore/ProjectionEntity.java | 16 +++++ .../com/google/gcloud/datastore/Query.java | 16 +++++ .../google/gcloud/datastore/QueryResult.java | 16 +++++ .../gcloud/datastore/QueryResultImpl.java | 16 +++++ .../com/google/gcloud/datastore/RawValue.java | 16 +++++ .../google/gcloud/datastore/Serializable.java | 16 +++++ .../google/gcloud/datastore/StringValue.java | 16 +++++ .../gcloud/datastore/StructuredQuery.java | 16 +++++ .../google/gcloud/datastore/Transaction.java | 16 +++++ .../gcloud/datastore/TransactionImpl.java | 16 +++++ .../gcloud/datastore/TransactionOption.java | 16 +++++ .../google/gcloud/datastore/Validator.java | 16 +++++ .../com/google/gcloud/datastore/Value.java | 16 +++++ .../google/gcloud/datastore/package-info.java | 16 +++++ .../java/com/google/gcloud/storage/Acl.java | 16 +++++ .../com/google/gcloud/storage/Bucket.java | 16 +++++ .../java/com/google/gcloud/storage/Key.java | 16 +++++ .../google/gcloud/storage/StorageObject.java | 16 +++++ .../google/gcloud/storage/StorageService.java | 16 +++++ .../gcloud/storage/StorageServiceFactory.java | 16 +++++ .../gcloud/storage/StorageServiceImpl.java | 16 +++++ .../gcloud/storage/StorageServiceOptions.java | 16 +++++ .../google/gcloud/storage/package-info.java | 16 +++++ .../google/gcloud/ExceptionHandlerTest.java | 16 +++++ .../com/google/gcloud/RetryHelperTest.java | 4 +- .../com/google/gcloud/RetryParamsTest.java | 16 +++++ .../DatastoreServiceIntegrationTest.java | 16 +++++ .../gcloud/datastore/LocalGcdHelper.java | 16 +++++ .../gcloud/datastore/SerializationTest.java | 16 +++++ 69 files changed, 1124 insertions(+), 34 deletions(-) diff --git a/.gitignore b/.gitignore index 7a46a67d0043..355e4b02954e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,12 @@ .gitignore -*.py[cod] -*.sw[op] - -# C extensions -*.so - # Packages -*.egg -*.egg-info dist build -eggs -parts bin var sdist -develop-eggs -.installed.cfg -lib -lib64 -__pycache__ - -# Installer logs -pip-log.txt +target # Unit test / coverage reports .coverage @@ -41,20 +24,10 @@ nosetests.xml .idea .settings .DS_Store -target # Built documentation -docs/_build - -# Virtual environment -env/ -coverage.xml - -# Regression test environment variables. -regression/local_test_setup +docs/ -# Make sure a generated file isn't accidentally committed. -pylintrc_reduced # Wheel directory used in Travis builds. -gcloud-python-wheels/ +gcloud-java-wheels/ diff --git a/README.md b/README.md index 157f813e551a..3b9e93c1492b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,63 @@ -git-demo -======== +Google Cloud for Java +===================== + +[![Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/gcloud-java) + +Java idiomatic client for Google Cloud Platform services. Supported APIs include: + + * Google Cloud Datastore + + +> Note: This package is a work-in-progress, and may occasionally +> make backwards-incompatible changes. + +Documentation and examples are available [here](https://github.com/GoogleCloudePlatform/gcloud-java/gh-pages/docs). + +## Google Cloud Datastore + +[Google Cloud Datastore][cloud-datastore] ([docs][cloud-datastore-docs]) is a fully +managed, schemaless database for storing non-relational data. Cloud Datastore +automatically scales with your users and supports ACID transactions, high availability +of reads and writes, strong consistency for reads and ancestor queries, and eventual +consistency for all other queries. + +Follow the [activation instructions][cloud-datastore-activation] to use the Google +Cloud Datastore API with your project. + + import com.google.gcloud.datastore.DatastoreService; + import com.google.gcloud.datastore.DatastoreServiceFactory; + import com.google.gcloud.datastore.DatastoreServiceOptions; + import com.google.gcloud.datastore.Entity; + import com.google.gcloud.datastore.Key; + import com.google.gcloud.datastore.KeyFactory; + + DatastoreServiceOptions options = DatastoreServiceOptions.builder().dataset("...").build(); + DatastoreService datastore = DatastoreServiceFactory.getDefault(options); + KeyFactory keyFactory = new KeyFactory(datastore).kind("..."); + Key key = keyFactory.newKey(keyName); + Entity entity = datastore.get(key); + if (entity == null) { + entity = Entity.builder(key) + .set("name", "John Do") + .set("age", 30) + .set("updated", false) + .build(); + datastore.put(entity); + } + +## Contributing + +Contributions are welcome. Please, see the +[CONTRIBUTING](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/CONTRIBUTING.md) +document for details. + +[cloud-datastore]: https://cloud.google.com/datastore/ +[cloud-datastore-docs]: https://cloud.google.com/datastore/docs +[cloud-datastore-activation]: https://cloud.google.com/datastore/docs/activate + +[cloud-pubsub]: https://cloud.google.com/pubsub/ +[cloud-pubsub-docs]: https://cloud.google.com/pubsub/docs + +[cloud-storage]: https://cloud.google.com/storage/ +[cloud-storage-docs]: https://cloud.google.com/storage/docs/overview +[cloud-storage-create-bucket]: https://cloud.google.com/storage/docs/cloud-console#_creatingbuckets diff --git a/src/main/java/com/google/gcloud/AuthConfig.java b/src/main/java/com/google/gcloud/AuthConfig.java index e1c5646c66d9..c2accdf438f8 100644 --- a/src/main/java/com/google/gcloud/AuthConfig.java +++ b/src/main/java/com/google/gcloud/AuthConfig.java @@ -1,3 +1,19 @@ +/* + * 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; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/src/main/java/com/google/gcloud/ExceptionHandler.java b/src/main/java/com/google/gcloud/ExceptionHandler.java index 7330a84ba06d..0e3f73d590fd 100644 --- a/src/main/java/com/google/gcloud/ExceptionHandler.java +++ b/src/main/java/com/google/gcloud/ExceptionHandler.java @@ -1,3 +1,19 @@ +/* + * 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; import static com.google.common.base.MoreObjects.firstNonNull; diff --git a/src/main/java/com/google/gcloud/RetryHelper.java b/src/main/java/com/google/gcloud/RetryHelper.java index 850048a7372d..7e354a4145d3 100644 --- a/src/main/java/com/google/gcloud/RetryHelper.java +++ b/src/main/java/com/google/gcloud/RetryHelper.java @@ -1,3 +1,19 @@ +/* + * 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; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/src/main/java/com/google/gcloud/RetryParams.java b/src/main/java/com/google/gcloud/RetryParams.java index 4da4c8399c8e..c7f25926eb9f 100644 --- a/src/main/java/com/google/gcloud/RetryParams.java +++ b/src/main/java/com/google/gcloud/RetryParams.java @@ -1,3 +1,19 @@ +/* + * 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; import static com.google.common.base.Preconditions.checkArgument; diff --git a/src/main/java/com/google/gcloud/ServiceOptions.java b/src/main/java/com/google/gcloud/ServiceOptions.java index b34875f5f87f..1c51b348abc6 100644 --- a/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/src/main/java/com/google/gcloud/ServiceOptions.java @@ -1,3 +1,19 @@ +/* + * 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; diff --git a/src/main/java/com/google/gcloud/datastore/BaseEntity.java b/src/main/java/com/google/gcloud/datastore/BaseEntity.java index a760d98fa90e..2a132a235bf8 100644 --- a/src/main/java/com/google/gcloud/datastore/BaseEntity.java +++ b/src/main/java/com/google/gcloud/datastore/BaseEntity.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.gcloud.datastore.BlobValue.of; diff --git a/src/main/java/com/google/gcloud/datastore/BaseKey.java b/src/main/java/com/google/gcloud/datastore/BaseKey.java index b96f817cbe29..90736614b5e1 100644 --- a/src/main/java/com/google/gcloud/datastore/BaseKey.java +++ b/src/main/java/com/google/gcloud/datastore/BaseKey.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.gcloud.datastore.Validator.validateDataset; diff --git a/src/main/java/com/google/gcloud/datastore/Batch.java b/src/main/java/com/google/gcloud/datastore/Batch.java index 705cec0b1139..0a78ad3dff71 100644 --- a/src/main/java/com/google/gcloud/datastore/Batch.java +++ b/src/main/java/com/google/gcloud/datastore/Batch.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; /** diff --git a/src/main/java/com/google/gcloud/datastore/BatchImpl.java b/src/main/java/com/google/gcloud/datastore/BatchImpl.java index 947f108aee7a..4f24c2ad77df 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchImpl.java +++ b/src/main/java/com/google/gcloud/datastore/BatchImpl.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.gcloud.datastore.DatastoreServiceException.throwInvalidRequest; diff --git a/src/main/java/com/google/gcloud/datastore/BatchOption.java b/src/main/java/com/google/gcloud/datastore/BatchOption.java index efcda4df4645..362a74e96c79 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchOption.java +++ b/src/main/java/com/google/gcloud/datastore/BatchOption.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import com.google.common.collect.ImmutableMap; diff --git a/src/main/java/com/google/gcloud/datastore/Blob.java b/src/main/java/com/google/gcloud/datastore/Blob.java index b8d6892d201f..42b79cc3650d 100644 --- a/src/main/java/com/google/gcloud/datastore/Blob.java +++ b/src/main/java/com/google/gcloud/datastore/Blob.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.common.base.Preconditions.checkArgument; diff --git a/src/main/java/com/google/gcloud/datastore/BlobValue.java b/src/main/java/com/google/gcloud/datastore/BlobValue.java index a495450a4967..4dcb80163ae4 100644 --- a/src/main/java/com/google/gcloud/datastore/BlobValue.java +++ b/src/main/java/com/google/gcloud/datastore/BlobValue.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.api.services.datastore.DatastoreV1.Value.BLOB_VALUE_FIELD_NUMBER; diff --git a/src/main/java/com/google/gcloud/datastore/BooleanValue.java b/src/main/java/com/google/gcloud/datastore/BooleanValue.java index 63de0e15791c..ac62d2d3fb27 100644 --- a/src/main/java/com/google/gcloud/datastore/BooleanValue.java +++ b/src/main/java/com/google/gcloud/datastore/BooleanValue.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.api.services.datastore.DatastoreV1.Value.BOOLEAN_VALUE_FIELD_NUMBER; diff --git a/src/main/java/com/google/gcloud/datastore/Cursor.java b/src/main/java/com/google/gcloud/datastore/Cursor.java index 25b35f6bcfe6..037e0ed7ed46 100644 --- a/src/main/java/com/google/gcloud/datastore/Cursor.java +++ b/src/main/java/com/google/gcloud/datastore/Cursor.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java b/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java index 453c3e8c6535..0b0558e565fd 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import com.google.common.collect.Maps; diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java index 29a975bda442..5c9ceb19cc35 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreReader.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreReader.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import java.util.Iterator; diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreReaderWriter.java b/src/main/java/com/google/gcloud/datastore/DatastoreReaderWriter.java index 52304f4e7e27..c64f86a8d0a3 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreReaderWriter.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreReaderWriter.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreService.java b/src/main/java/com/google/gcloud/datastore/DatastoreService.java index c4e71bcbc010..9770005dbc80 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreService.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreService.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import java.util.List; diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java index b62df718fa61..9f1b4d6ad757 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceException.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import com.google.api.services.datastore.client.DatastoreException; diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java index 9a8bb1dd82bc..ca87e9cdad9b 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceFactory.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java index 5070c21e613a..c100e45e548a 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceImpl.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import com.google.api.services.datastore.DatastoreV1; diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java index 262d8f81060f..56acdb340c28 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreServiceOptions.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.common.base.MoreObjects.firstNonNull; diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java b/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java index 5c87a49424ba..69e911c65e14 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; /** diff --git a/src/main/java/com/google/gcloud/datastore/DateTime.java b/src/main/java/com/google/gcloud/datastore/DateTime.java index a2e8c6398351..3b7e85a2e736 100644 --- a/src/main/java/com/google/gcloud/datastore/DateTime.java +++ b/src/main/java/com/google/gcloud/datastore/DateTime.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/src/main/java/com/google/gcloud/datastore/DateTimeValue.java b/src/main/java/com/google/gcloud/datastore/DateTimeValue.java index 666dd24c2dfb..f6c25b9d0353 100644 --- a/src/main/java/com/google/gcloud/datastore/DateTimeValue.java +++ b/src/main/java/com/google/gcloud/datastore/DateTimeValue.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.api.services.datastore.DatastoreV1.Value.TIMESTAMP_MICROSECONDS_VALUE_FIELD_NUMBER; diff --git a/src/main/java/com/google/gcloud/datastore/DoubleValue.java b/src/main/java/com/google/gcloud/datastore/DoubleValue.java index b5c19f3545c6..8c1e7b1f18ed 100644 --- a/src/main/java/com/google/gcloud/datastore/DoubleValue.java +++ b/src/main/java/com/google/gcloud/datastore/DoubleValue.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.api.services.datastore.DatastoreV1.Value.DOUBLE_VALUE_FIELD_NUMBER; diff --git a/src/main/java/com/google/gcloud/datastore/Entity.java b/src/main/java/com/google/gcloud/datastore/Entity.java index 292291792442..95d96ac8030a 100644 --- a/src/main/java/com/google/gcloud/datastore/Entity.java +++ b/src/main/java/com/google/gcloud/datastore/Entity.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/src/main/java/com/google/gcloud/datastore/EntityValue.java b/src/main/java/com/google/gcloud/datastore/EntityValue.java index c4e46bebc8ae..cdf24d3ca8ed 100644 --- a/src/main/java/com/google/gcloud/datastore/EntityValue.java +++ b/src/main/java/com/google/gcloud/datastore/EntityValue.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.api.services.datastore.DatastoreV1.Value.ENTITY_VALUE_FIELD_NUMBER; diff --git a/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/src/main/java/com/google/gcloud/datastore/GqlQuery.java index 9c5c982b82c4..7ab0831c7e30 100644 --- a/src/main/java/com/google/gcloud/datastore/GqlQuery.java +++ b/src/main/java/com/google/gcloud/datastore/GqlQuery.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/src/main/java/com/google/gcloud/datastore/Key.java b/src/main/java/com/google/gcloud/datastore/Key.java index 43ea51d83c1c..97559478293c 100644 --- a/src/main/java/com/google/gcloud/datastore/Key.java +++ b/src/main/java/com/google/gcloud/datastore/Key.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static java.nio.charset.StandardCharsets.UTF_8; diff --git a/src/main/java/com/google/gcloud/datastore/KeyFactory.java b/src/main/java/com/google/gcloud/datastore/KeyFactory.java index 08d854404a3b..90be65882a44 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyFactory.java +++ b/src/main/java/com/google/gcloud/datastore/KeyFactory.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/src/main/java/com/google/gcloud/datastore/KeyValue.java b/src/main/java/com/google/gcloud/datastore/KeyValue.java index 65163103ede4..959deb0831d4 100644 --- a/src/main/java/com/google/gcloud/datastore/KeyValue.java +++ b/src/main/java/com/google/gcloud/datastore/KeyValue.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.api.services.datastore.DatastoreV1.Value.KEY_VALUE_FIELD_NUMBER; diff --git a/src/main/java/com/google/gcloud/datastore/ListValue.java b/src/main/java/com/google/gcloud/datastore/ListValue.java index 4132858432b4..abf7ff9c9a40 100644 --- a/src/main/java/com/google/gcloud/datastore/ListValue.java +++ b/src/main/java/com/google/gcloud/datastore/ListValue.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.api.services.datastore.DatastoreV1.Value.LIST_VALUE_FIELD_NUMBER; diff --git a/src/main/java/com/google/gcloud/datastore/LongValue.java b/src/main/java/com/google/gcloud/datastore/LongValue.java index ac887e4c7263..e69d87d05e04 100644 --- a/src/main/java/com/google/gcloud/datastore/LongValue.java +++ b/src/main/java/com/google/gcloud/datastore/LongValue.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.api.services.datastore.DatastoreV1.Value.INTEGER_VALUE_FIELD_NUMBER; diff --git a/src/main/java/com/google/gcloud/datastore/NullValue.java b/src/main/java/com/google/gcloud/datastore/NullValue.java index 58c91e8342cb..77c813c9068b 100644 --- a/src/main/java/com/google/gcloud/datastore/NullValue.java +++ b/src/main/java/com/google/gcloud/datastore/NullValue.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.common.base.Preconditions.checkArgument; diff --git a/src/main/java/com/google/gcloud/datastore/PartialEntity.java b/src/main/java/com/google/gcloud/datastore/PartialEntity.java index b774e30c2a3d..1831d810e831 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialEntity.java +++ b/src/main/java/com/google/gcloud/datastore/PartialEntity.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import com.google.api.services.datastore.DatastoreV1; diff --git a/src/main/java/com/google/gcloud/datastore/PartialKey.java b/src/main/java/com/google/gcloud/datastore/PartialKey.java index 74659534b3c1..ae4c706f76c7 100644 --- a/src/main/java/com/google/gcloud/datastore/PartialKey.java +++ b/src/main/java/com/google/gcloud/datastore/PartialKey.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import com.google.api.services.datastore.DatastoreV1; diff --git a/src/main/java/com/google/gcloud/datastore/PathElement.java b/src/main/java/com/google/gcloud/datastore/PathElement.java index b3ec8b55e5d3..186ed97adcde 100644 --- a/src/main/java/com/google/gcloud/datastore/PathElement.java +++ b/src/main/java/com/google/gcloud/datastore/PathElement.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.common.base.Preconditions.checkArgument; diff --git a/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java b/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java index 22201f82dda4..7d62ee88405f 100644 --- a/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java +++ b/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import com.google.api.services.datastore.DatastoreV1; diff --git a/src/main/java/com/google/gcloud/datastore/Query.java b/src/main/java/com/google/gcloud/datastore/Query.java index 17084249c646..a8f4c97fdc72 100644 --- a/src/main/java/com/google/gcloud/datastore/Query.java +++ b/src/main/java/com/google/gcloud/datastore/Query.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/src/main/java/com/google/gcloud/datastore/QueryResult.java b/src/main/java/com/google/gcloud/datastore/QueryResult.java index 95fb993b1771..65d087d4b4a9 100644 --- a/src/main/java/com/google/gcloud/datastore/QueryResult.java +++ b/src/main/java/com/google/gcloud/datastore/QueryResult.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import java.util.Iterator; diff --git a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java index f1f6ee2f4838..2944724e116b 100644 --- a/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java +++ b/src/main/java/com/google/gcloud/datastore/QueryResultImpl.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import com.google.api.services.datastore.DatastoreV1; diff --git a/src/main/java/com/google/gcloud/datastore/RawValue.java b/src/main/java/com/google/gcloud/datastore/RawValue.java index 17e5b70b12ab..b2e8b9785dd8 100644 --- a/src/main/java/com/google/gcloud/datastore/RawValue.java +++ b/src/main/java/com/google/gcloud/datastore/RawValue.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import com.google.api.services.datastore.DatastoreV1; diff --git a/src/main/java/com/google/gcloud/datastore/Serializable.java b/src/main/java/com/google/gcloud/datastore/Serializable.java index 3770ecfccf7f..ff62fe89195f 100644 --- a/src/main/java/com/google/gcloud/datastore/Serializable.java +++ b/src/main/java/com/google/gcloud/datastore/Serializable.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import com.google.protobuf.GeneratedMessage; diff --git a/src/main/java/com/google/gcloud/datastore/StringValue.java b/src/main/java/com/google/gcloud/datastore/StringValue.java index ae09e39b6b6e..699d2bd97cd7 100644 --- a/src/main/java/com/google/gcloud/datastore/StringValue.java +++ b/src/main/java/com/google/gcloud/datastore/StringValue.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.api.services.datastore.DatastoreV1.Value.STRING_VALUE_FIELD_NUMBER; diff --git a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java index 671e7403b511..d86086f53088 100644 --- a/src/main/java/com/google/gcloud/datastore/StructuredQuery.java +++ b/src/main/java/com/google/gcloud/datastore/StructuredQuery.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.api.client.util.Preconditions.checkNotNull; diff --git a/src/main/java/com/google/gcloud/datastore/Transaction.java b/src/main/java/com/google/gcloud/datastore/Transaction.java index fea662ead76a..099248465587 100644 --- a/src/main/java/com/google/gcloud/datastore/Transaction.java +++ b/src/main/java/com/google/gcloud/datastore/Transaction.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import java.util.Iterator; diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java index 1b33ae19b4d8..86f5a8908a22 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.gcloud.datastore.DatastoreServiceException.throwInvalidRequest; diff --git a/src/main/java/com/google/gcloud/datastore/TransactionOption.java b/src/main/java/com/google/gcloud/datastore/TransactionOption.java index 433787a02f69..c1c8368213dc 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionOption.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionOption.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import com.google.api.services.datastore.DatastoreV1; diff --git a/src/main/java/com/google/gcloud/datastore/Validator.java b/src/main/java/com/google/gcloud/datastore/Validator.java index d2586573d9e8..9ee6a5e57d68 100644 --- a/src/main/java/com/google/gcloud/datastore/Validator.java +++ b/src/main/java/com/google/gcloud/datastore/Validator.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.common.base.Preconditions.checkArgument; diff --git a/src/main/java/com/google/gcloud/datastore/Value.java b/src/main/java/com/google/gcloud/datastore/Value.java index ff064e87178e..2db172039535 100644 --- a/src/main/java/com/google/gcloud/datastore/Value.java +++ b/src/main/java/com/google/gcloud/datastore/Value.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static com.google.common.base.Preconditions.checkArgument; diff --git a/src/main/java/com/google/gcloud/datastore/package-info.java b/src/main/java/com/google/gcloud/datastore/package-info.java index 88933a598ca9..7661deebb82c 100644 --- a/src/main/java/com/google/gcloud/datastore/package-info.java +++ b/src/main/java/com/google/gcloud/datastore/package-info.java @@ -1,3 +1,19 @@ +/* + * 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. + */ + /** * A client to the Google Cloud Datastore. * diff --git a/src/main/java/com/google/gcloud/storage/Acl.java b/src/main/java/com/google/gcloud/storage/Acl.java index a0ef42844b7d..4c218848ea9a 100644 --- a/src/main/java/com/google/gcloud/storage/Acl.java +++ b/src/main/java/com/google/gcloud/storage/Acl.java @@ -1,3 +1,19 @@ +/* + * 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.storage; public interface Acl { diff --git a/src/main/java/com/google/gcloud/storage/Bucket.java b/src/main/java/com/google/gcloud/storage/Bucket.java index be2653494fd9..5fcd7f4abe50 100644 --- a/src/main/java/com/google/gcloud/storage/Bucket.java +++ b/src/main/java/com/google/gcloud/storage/Bucket.java @@ -1,3 +1,19 @@ +/* + * 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.storage; import java.nio.ByteBuffer; diff --git a/src/main/java/com/google/gcloud/storage/Key.java b/src/main/java/com/google/gcloud/storage/Key.java index 759cc5e9496f..1e16d985c021 100644 --- a/src/main/java/com/google/gcloud/storage/Key.java +++ b/src/main/java/com/google/gcloud/storage/Key.java @@ -1,3 +1,19 @@ +/* + * 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.storage; public class Key { diff --git a/src/main/java/com/google/gcloud/storage/StorageObject.java b/src/main/java/com/google/gcloud/storage/StorageObject.java index 0fbee66031b8..0bbdd3b20cc1 100644 --- a/src/main/java/com/google/gcloud/storage/StorageObject.java +++ b/src/main/java/com/google/gcloud/storage/StorageObject.java @@ -1,3 +1,19 @@ +/* + * 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.storage; import java.nio.ByteBuffer; diff --git a/src/main/java/com/google/gcloud/storage/StorageService.java b/src/main/java/com/google/gcloud/storage/StorageService.java index 47e2af985f3a..55b9b84c1e2b 100644 --- a/src/main/java/com/google/gcloud/storage/StorageService.java +++ b/src/main/java/com/google/gcloud/storage/StorageService.java @@ -1,3 +1,19 @@ +/* + * 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.storage; public interface StorageService { diff --git a/src/main/java/com/google/gcloud/storage/StorageServiceFactory.java b/src/main/java/com/google/gcloud/storage/StorageServiceFactory.java index acc243b9ba38..7f94334b3c08 100644 --- a/src/main/java/com/google/gcloud/storage/StorageServiceFactory.java +++ b/src/main/java/com/google/gcloud/storage/StorageServiceFactory.java @@ -1,3 +1,19 @@ +/* + * 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.storage; diff --git a/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java b/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java index 85c57bea3a9b..45a347d27c6d 100644 --- a/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java +++ b/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java @@ -1,3 +1,19 @@ +/* + * 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.storage; import com.google.api.services.storage.Storage; diff --git a/src/main/java/com/google/gcloud/storage/StorageServiceOptions.java b/src/main/java/com/google/gcloud/storage/StorageServiceOptions.java index 6cdcdc65bf0c..a4a8e60adf94 100644 --- a/src/main/java/com/google/gcloud/storage/StorageServiceOptions.java +++ b/src/main/java/com/google/gcloud/storage/StorageServiceOptions.java @@ -1,3 +1,19 @@ +/* + * 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.storage; import com.google.api.client.json.jackson.JacksonFactory; diff --git a/src/main/java/com/google/gcloud/storage/package-info.java b/src/main/java/com/google/gcloud/storage/package-info.java index 0f78d1bab46a..9aabec93c93f 100644 --- a/src/main/java/com/google/gcloud/storage/package-info.java +++ b/src/main/java/com/google/gcloud/storage/package-info.java @@ -1,3 +1,19 @@ +/* + * 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. + */ + /** * A client to Google Cloud Storage. * diff --git a/src/test/java/com/google/gcloud/ExceptionHandlerTest.java b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java index 960f293e0baa..1d96e86d0e46 100644 --- a/src/test/java/com/google/gcloud/ExceptionHandlerTest.java +++ b/src/test/java/com/google/gcloud/ExceptionHandlerTest.java @@ -1,3 +1,19 @@ +/* + * 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; import static org.junit.Assert.assertFalse; diff --git a/src/test/java/com/google/gcloud/RetryHelperTest.java b/src/test/java/com/google/gcloud/RetryHelperTest.java index b8b0f7a4c501..dfd933bcae46 100644 --- a/src/test/java/com/google/gcloud/RetryHelperTest.java +++ b/src/test/java/com/google/gcloud/RetryHelperTest.java @@ -1,11 +1,11 @@ /* - * Copyright 2012 Google Inc. All Rights Reserved. + * 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 + * 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, diff --git a/src/test/java/com/google/gcloud/RetryParamsTest.java b/src/test/java/com/google/gcloud/RetryParamsTest.java index 744bec7b59a2..d1d5e3c076d8 100644 --- a/src/test/java/com/google/gcloud/RetryParamsTest.java +++ b/src/test/java/com/google/gcloud/RetryParamsTest.java @@ -1,3 +1,19 @@ +/* + * 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; import static com.google.gcloud.RetryParams.DEFAULT_INITIAL_RETRY_DELAY_MILLIS; diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java index 4af76c5ca07e..8bc31d5381c3 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static org.junit.Assert.assertEquals; diff --git a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java index 279cb0b2392e..5a71442b8a6b 100644 --- a/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java +++ b/src/test/java/com/google/gcloud/datastore/LocalGcdHelper.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static java.nio.charset.StandardCharsets.UTF_8; diff --git a/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 44fb398bdfb6..89b7b01aadb3 100644 --- a/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -1,3 +1,19 @@ +/* + * 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.datastore; import static java.nio.charset.StandardCharsets.UTF_8; From 33acc849dc3d5caf3ac52d79791dee8bb6af74ab Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 20 Jan 2015 15:27:56 -0800 Subject: [PATCH 094/771] adding contributing --- CONTRIBUTING.md | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000000..df4129b3ed6d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,50 @@ +Contributing +============ + +1. **Please sign one of the contributor license agreements below.** +1. Fork the repo, develop and test your code changes, add docs. +1. Make sure that your commit messages clearly describe the changes. +1. Send a pull request. + + +Here are some guidelines for hacking on gcloud-java. + + +Using maven for build/test +-------------------------- +After you cloned the repository use Maven for building and running the tests. +Maven 3.0+ is required. + + +Adding Features +--------------- +In order to add a feature to gcloud-java: + +The feature must be fully documented using Javadoc and examples should be provided. +The feature must work fully on Java 7 and above. +The feature must not add unnecessary dependencies (where "unnecessary" is of course subjective, +but new dependencies should be discussed). + + +Coding Style +------------ +Maintain the coding style in the project and in particular the modified files. +Follow the Google Java [style](http://google-styleguide.googlecode.com/svn/trunk/javaguide.html). + + +## Contributor License Agreements + +Before we can accept your pull requests you'll need to sign a Contributor +License Agreement (CLA): + +- **If you are an individual writing original source code** and **you own the intellectual property**, +then you'll need to sign an [individual CLA][indvcla]. +- **If you work for a company that wants to allow you to contribute your work**, +then you'll need to sign a [corporate CLA][corpcla]. + +You can sign these electronically (just scroll to the bottom). After that, +we'll be able to accept your pull requests. + +[gcloudcli]: https://developers.google.com/cloud/sdk/gcloud/ +[indvcla]: https://developers.google.com/open-source/cla/individual +[corpcla]: https://developers.google.com/open-source/cla/corporate diff --git a/README.md b/README.md index 3b9e93c1492b..bf88957bae80 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Java idiomatic client for Google Cloud Platform services. Supported APIs include > Note: This package is a work-in-progress, and may occasionally > make backwards-incompatible changes. -Documentation and examples are available [here](https://github.com/GoogleCloudePlatform/gcloud-java/gh-pages/docs). +Documentation and examples are available [here](https://github.com/GoogleCloudePlatform/gcloud-java/gh-pages/apidocs). ## Google Cloud Datastore From cb0709d0ecf4b781d05a87116b727c8e95cb91c9 Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 20 Jan 2015 15:53:46 -0800 Subject: [PATCH 095/771] adding travis config file --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000000..b2168ad38271 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: java +jdk: + - oraclejdk8 + - oraclejdk7 + - openjdk7 From 30eb51cbf99c58c3d157d59c867326207c108636 Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 20 Jan 2015 16:04:37 -0800 Subject: [PATCH 096/771] Make travis run verify instead of test --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index b2168ad38271..1b9769d8b85e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,5 @@ jdk: - oraclejdk8 - oraclejdk7 - openjdk7 +install: mvn install -DskipTests=true +script: mvn verify From 9c1f46180c8a38760c160886cb3e57a0bca2c9e5 Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 20 Jan 2015 16:28:44 -0800 Subject: [PATCH 097/771] change maven group information --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 69c2673fe060..a45df9dff031 100644 --- a/pom.xml +++ b/pom.xml @@ -2,8 +2,8 @@ 4.0.0 - com.ozarov.testing - git-demo + com.google.gcloud + gcloud-java 0.0.1-SNAPSHOT From e0113f30b6d767bff72257b8f6f821d4a5149caa Mon Sep 17 00:00:00 2001 From: aozarov Date: Mon, 9 Feb 2015 15:29:58 -0800 Subject: [PATCH 098/771] adding travis credentials --- .travis.yml | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1b9769d8b85e..5997f22ead37 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,21 @@ -language: java +nguage: java jdk: - - oraclejdk8 - - oraclejdk7 - - openjdk7 +- oraclejdk8 +- oraclejdk7 +- openjdk7 +before_install: +- mvn clean +- git clone -b travis `git config --get remote.origin.url` target/travis +- cp target/travis/settings.xml ~/.m2/settings.xml install: mvn install -DskipTests=true script: mvn verify +branches: + only: + - master +after_success: +- mvn cobertura:cobertura coveralls:report +- mvn site --settings target/travis/settings.xml +env: + global: + - secure: bjyc4GJSP9850m6KSO2LiGKMJI/iFJ6dIDNrrZJHiokWUv8ID5+X7O04YtAFF+WrYyVDJ8Zs+uduAJaQ5NFesnhFjMMNTOaliYIBjpBgdZU0vgmsU0NzO35bu6wA5DAdI8AGUNCVwSZpOAMnj/80dbYbyFwBn2DWBZ3QwpV6J/I= + - secure: CUM2l73KFm7U4eDsUKkh1WyEUzF3v94Ltvs7MnKU9olE1dNp3YmRBL9Lqhx3hSDqm/xv0ETQsPy29Fs2+VFkhQQxSley6iS/4trr2fioTB680txfXo/zDdmGSP1q1/U40fv1S+jvuBRAhDV5W+8dhWOGtzMH0tJp/TszeDGlmCY= From 72551d224965f1ab92bc6f46c582884b4849cfa1 Mon Sep 17 00:00:00 2001 From: aozarov Date: Mon, 9 Feb 2015 15:41:56 -0800 Subject: [PATCH 099/771] apply changes from git-demo --- README.md | 3 +- checkstyle.xml | 363 ++++++++++-------- pom.xml | 217 ++++++++--- .../datastore/BaseDatastoreBatchWriter.java | 169 ++++++++ .../com/google/gcloud/datastore/Batch.java | 5 +- .../google/gcloud/datastore/BatchImpl.java | 159 +------- .../datastore/DatastoreBatchWriter.java | 6 - .../google/gcloud/datastore/Transaction.java | 4 +- .../gcloud/datastore/TransactionImpl.java | 89 ++--- .../com/google/gcloud/storage/Bucket.java | 26 +- .../storage/{Key.java => InputChannel.java} | 33 +- .../google/gcloud/storage/OutputChannel.java | 33 ++ .../google/gcloud/storage/StorageObject.java | 65 +++- .../google/gcloud/storage/StorageService.java | 2 +- src/site/apt/index.apt | 23 ++ src/site/site.xml | 11 + ...ionTest.java => DatastoreServiceTest.java} | 21 +- 17 files changed, 764 insertions(+), 465 deletions(-) create mode 100644 src/main/java/com/google/gcloud/datastore/BaseDatastoreBatchWriter.java rename src/main/java/com/google/gcloud/storage/{Key.java => InputChannel.java} (57%) create mode 100644 src/main/java/com/google/gcloud/storage/OutputChannel.java create mode 100644 src/site/apt/index.apt create mode 100644 src/site/site.xml rename src/test/java/com/google/gcloud/datastore/{DatastoreServiceIntegrationTest.java => DatastoreServiceTest.java} (98%) diff --git a/README.md b/README.md index bf88957bae80..7e9c47ebd268 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ Google Cloud for Java ===================== [![Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/gcloud-java) +[![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/gcloud-java/badge.svg)](https://coveralls.io/r/GoogleCloudPlatform/gcloud-java) Java idiomatic client for Google Cloud Platform services. Supported APIs include: @@ -11,7 +12,7 @@ Java idiomatic client for Google Cloud Platform services. Supported APIs include > Note: This package is a work-in-progress, and may occasionally > make backwards-incompatible changes. -Documentation and examples are available [here](https://github.com/GoogleCloudePlatform/gcloud-java/gh-pages/apidocs). +Documentation and examples are available [here](https://googlecloudeplatform.github.com/gcloud-java/apidocs). ## Google Cloud Datastore diff --git a/checkstyle.xml b/checkstyle.xml index 814915090b25..7b6d2abd5db8 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -1,171 +1,200 @@ - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Checkstyle configuration that checks the Google coding conventions from: + + - Google Java Style + https://google-styleguide.googlecode.com/svn-history/r130/trunk/javaguide.html + + Checkstyle is very configurable. Be sure to read the documentation at + http://checkstyle.sf.net (or in your downloaded distribution). + + Most Checks are configurable, be sure to consult the documentation. + + To completely disable a check, just comment it out or delete it from the file. + + Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov. + + --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - diff --git a/pom.xml b/pom.xml index a45df9dff031..f9a5dc52f8b9 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,29 @@ - + 4.0.0 com.google.gcloud gcloud-java + jar 0.0.1-SNAPSHOT + GCloud Java + https://github.com/GoogleCloudPlatform/gcloud-java + + Java idiomatic client for Google Cloud Platform services. + + + scm:git:git@github.com:GoogleCloudPlatform/gcloud-java.git + scm:git:git@github.com:GoogleCloudPlatform/gcloud-java.git + https://github.com/GoogleCloudPlatform/gcloud-java + HEAD + + + Travis CI + https://travis-ci.org/GoogleCloudPlatform/gcloud-java + + + https://github.com/GoogleCloudPlatform/gcloud-java/issues + GitHub Issues + com.google.http-client @@ -87,9 +106,16 @@ http://repo.maven.apache.org/maven2 + + + GCloud Java Software License + https://raw.github.com/GoogleCloudPlatform/gcloud-java/master/LICENSE.md + + UTF-8 UTF-8 + github @@ -106,70 +132,10 @@ - - maven-antrun-plugin - 1.8 - - - before-integration-test - pre-integration-test - - - - - - - - - - - - - run - - - - after-integration-test - post-integration-test - - - - - - - - - - - - - run - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.18.1 - - - **/*IntegrationTest.java - - - org.apache.maven.plugins maven-failsafe-plugin 2.18.1 - - - none - - - **/*IntegrationTest.java - - @@ -192,6 +158,133 @@ UTF-8 + + org.eluder.coveralls + coveralls-maven-plugin + 3.0.1 + + + org.codehaus.mojo + cobertura-maven-plugin + 2.6 + + + xml + html + + true + + true + + com/google/gcloud/**/*.class + + + + 256m + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.13 + + + com.puppycrawl.tools + checkstyle + 6.2 + + + + + org.apache.maven.plugins + maven-site-plugin + 3.4 + + + + org.apache.maven.plugins + maven-changelog-plugin + 2.3 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.8 + + + + dependencies + project-team + mailing-list + cim + issue-tracking + license + scm + + + + + true + true + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.1 + + + html + + javadoc + + + + + protected + true + ${project.build.directory}/javadoc + + + + org.apache.maven.plugins + maven-surefire-report-plugin + 2.18.1 + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.13 + + checkstyle.xml + false + + + + org.codehaus.mojo + cobertura-maven-plugin + 2.6 + + + + + + com.github.github + site-maven-plugin + 0.10 + + Creating site for ${project.artifactId} ${project.version} + + + + + site + + site + + + diff --git a/src/main/java/com/google/gcloud/datastore/BaseDatastoreBatchWriter.java b/src/main/java/com/google/gcloud/datastore/BaseDatastoreBatchWriter.java new file mode 100644 index 000000000000..f2f60ef5d30f --- /dev/null +++ b/src/main/java/com/google/gcloud/datastore/BaseDatastoreBatchWriter.java @@ -0,0 +1,169 @@ +/* + * 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.datastore; + +import com.google.api.services.datastore.DatastoreV1; + +import java.util.*; + +/** + * Base class for DatastoreBatchWriter. + */ +public abstract class BaseDatastoreBatchWriter implements DatastoreBatchWriter { + + private final String name; + private final Map toAdd = new LinkedHashMap<>(); + private final List toAddAutoId = new LinkedList<>(); + private final Map toUpdate = new LinkedHashMap<>(); + private final Map toPut = new LinkedHashMap<>(); + private final Set toDelete = new LinkedHashSet<>(); + private boolean active = true; + + protected BaseDatastoreBatchWriter(String name) { + this.name = name; + } + + @Override + public void add(Entity... entities) { + validateActive(); + for (Entity entity : entities) { + Key key = entity.key(); + if (toAdd.containsKey(key) || toUpdate.containsKey(key) || toPut.containsKey(key)) { + throw newInvalidRequest("Entity with the key %s was already added or updated in this %s", + entity.key(), name); + } + if (toDelete.remove(key)) { + toPut.put(key, entity); + } else { + toAdd.put(key, entity); + } + } + } + + @Override + public void add(PartialEntity... entities) { + validateActive(); + for (PartialEntity entity : entities) { + if (entity instanceof Entity) { + add((Entity) entity); + } else { + toAddAutoId.add(entity); + } + } + } + + @Override + public void update(Entity... entities) { + validateActive(); + for (Entity entity : entities) { + Key key = entity.key(); + if (toDelete.contains(key)) { + throw newInvalidRequest("Entity with the key %s was already deleted in this %s", + entity.key(), name); + } + if (toAdd.remove(key) != null || toPut.containsKey(key)) { + toPut.put(key, entity); + } else { + toUpdate.put(key, entity); + } + } + } + + @Override + public void put(Entity... entities) { + validateActive(); + for (Entity entity : entities) { + Key key = entity.key(); + toAdd.remove(key); + toUpdate.remove(key); + toDelete.remove(key); + toPut.put(key, entity); + } + } + + @Override + public void delete(Key... keys) { + validateActive(); + for (Key key : keys) { + toAdd.remove(key); + toUpdate.remove(key); + toPut.remove(key); + toDelete.add(key); + } + } + + @Override + public boolean active() { + return active; + } + + protected String name() { + return name; + } + + protected Map toAdd() { + return toAdd; + } + + protected List toAddAutoId() { + return toAddAutoId; + } + + protected Map toUpdate() { + return toUpdate; + } + + protected Map toPut() { + return toPut; + } + + protected Set toDelete() { + return toDelete; + } + + protected void deactivate() { + active = false; + } + + protected void validateActive() { + if (!active) { + throw newInvalidRequest("%s is no longer active", name); + } + } + + protected DatastoreServiceException newInvalidRequest(String msg, Object... params) { + return DatastoreServiceException.throwInvalidRequest(String.format(msg, params)); + } + + protected DatastoreV1.Mutation.Builder toMutationPb() { + DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); + for (PartialEntity entity : toAddAutoId()) { + mutationPb.addInsertAutoId(entity.toPb()); + } + for (Entity entity : toAdd().values()) { + mutationPb.addInsert(entity.toPb()); + } + for (Entity entity : toUpdate().values()) { + mutationPb.addUpdate(entity.toPb()); + } + for (Entity entity : toPut().values()) { + mutationPb.addUpsert(entity.toPb()); + } + for (Key key : toDelete()) { + mutationPb.addDelete(key.toPb()); + } + return mutationPb; + } +} diff --git a/src/main/java/com/google/gcloud/datastore/Batch.java b/src/main/java/com/google/gcloud/datastore/Batch.java index 0a78ad3dff71..a6ba78160ef4 100644 --- a/src/main/java/com/google/gcloud/datastore/Batch.java +++ b/src/main/java/com/google/gcloud/datastore/Batch.java @@ -16,6 +16,8 @@ package com.google.gcloud.datastore; +import java.util.List; + /** * An interface to represent a batch of write operations. * Any write operation that is applied on a batch will only be sent @@ -34,7 +36,8 @@ */ public interface Batch extends DatastoreBatchWriter { - interface Response extends DatastoreBatchWriter.Response { + interface Response { + List generatedKeys(); } /** diff --git a/src/main/java/com/google/gcloud/datastore/BatchImpl.java b/src/main/java/com/google/gcloud/datastore/BatchImpl.java index 4f24c2ad77df..7139ca3bb1c6 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchImpl.java +++ b/src/main/java/com/google/gcloud/datastore/BatchImpl.java @@ -1,47 +1,34 @@ /* * 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 + * 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 + * 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. + * 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.datastore; -import static com.google.gcloud.datastore.DatastoreServiceException.throwInvalidRequest; - import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Function; import com.google.common.collect.Lists; import com.google.gcloud.datastore.BatchOption.ForceWrites; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; -class BatchImpl implements Batch { - private final Map toAdd = new LinkedHashMap<>(); - private final List toAddAutoId = new LinkedList<>(); - private final Map toUpdate = new LinkedHashMap<>(); - private final Map toPut = new LinkedHashMap<>(); - private final Set toDelete = new LinkedHashSet<>(); +class BatchImpl extends BaseDatastoreBatchWriter implements Batch { + + private final DatastoreServiceImpl datastore; private final boolean force; - final DatastoreServiceImpl datastore; - private boolean active = true; - static class ResponseImpl implements Response { + static class ResponseImpl implements Batch.Response { private final DatastoreV1.CommitResponse response; @@ -61,9 +48,9 @@ public List generatedKeys() { } BatchImpl(DatastoreServiceImpl datastore, BatchOption... options) { + super("batch"); this.datastore = datastore; - Map, BatchOption> optionsMap = - BatchOption.asImmutableMap(options); + Map, BatchOption> optionsMap = BatchOption.asImmutableMap(options); if (optionsMap.containsKey(ForceWrites.class)) { force = ((ForceWrites) optionsMap.get(ForceWrites.class)).force(); } else { @@ -71,126 +58,18 @@ public List generatedKeys() { } } - void validateActive() { - if (!active) { - throw throwInvalidRequest(getName() + " is no longer active"); - } - } - - String getName() { - return "batch"; - } - - @Override - public void add(Entity... entities) { - validateActive(); - for (Entity entity : entities) { - Key key = entity.key(); - if (toAdd.containsKey(key) || toUpdate.containsKey(key) || toPut.containsKey(key)) { - throw throwInvalidRequest("Entity with the key %s was already added or updated in this " - + getName(), entity.key()); - } - if (toDelete.remove(key)) { - toPut.put(key, entity); - } else { - toAdd.put(key, entity); - } - } - } - - @Override - public void add(PartialEntity... entities) { - validateActive(); - for (PartialEntity entity : entities) { - if (entity instanceof Entity) { - add((Entity) entity); - } else { - toAddAutoId.add(entity); - } - } - } - - @Override - public void update(Entity... entities) { - validateActive(); - for (Entity entity : entities) { - Key key = entity.key(); - if (toDelete.contains(key)) { - throw throwInvalidRequest( - "Entity with the key %s was already deleted in this " + getName(), entity.key()); - } - if (toAdd.remove(key) != null || toPut.containsKey(key)) { - toPut.put(key, entity); - } else { - toUpdate.put(key, entity); - } - } - } - - @Override - public void put(Entity... entities) { - validateActive(); - for (Entity entity : entities) { - Key key = entity.key(); - toAdd.remove(key); - toUpdate.remove(key); - toDelete.remove(key); - toPut.put(key, entity); - } - } - - @Override - public void delete(Key... keys) { - validateActive(); - for (Key key : keys) { - toAdd.remove(key); - toUpdate.remove(key); - toPut.remove(key); - toDelete.add(key); - } - } - @Override - public Response submit() { - return new ResponseImpl(commitRequest()); - } - - DatastoreV1.CommitResponse commitRequest() { + public Batch.Response submit() { validateActive(); - DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); - for (PartialEntity entity : toAddAutoId) { - mutationPb.addInsertAutoId(entity.toPb()); - } - for (Entity entity : toAdd.values()) { - mutationPb.addInsert(entity.toPb()); - } - for (Entity entity : toUpdate.values()) { - mutationPb.addUpdate(entity.toPb()); - } - for (Entity entity : toPut.values()) { - mutationPb.addUpsert(entity.toPb()); - } - for (Key key : toDelete) { - mutationPb.addDelete(key.toPb()); - } + DatastoreV1.Mutation.Builder mutationPb = toMutationPb(); if (force) { mutationPb.setForce(force); } - DatastoreV1.CommitRequest.Builder requestPb = newCommitRequest(); - requestPb.setMutation(mutationPb); - DatastoreV1.CommitResponse response = datastore.commit(requestPb.build()); - active = false; - return response; - } - - @Override - public boolean active() { - return active; - } - - DatastoreV1.CommitRequest.Builder newCommitRequest() { DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder(); requestPb.setMode(DatastoreV1.CommitRequest.Mode.NON_TRANSACTIONAL); - return requestPb; + requestPb.setMutation(mutationPb); + DatastoreV1.CommitResponse responsePb = datastore.commit(requestPb.build()); + deactivate(); + return new ResponseImpl(responsePb); } } diff --git a/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java b/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java index 25c2409e58ea..dce06fb56304 100644 --- a/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java +++ b/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java @@ -16,18 +16,12 @@ package com.google.gcloud.datastore; -import java.util.List; - /** * An interface to represent a batch of write operations. * All write operation for a batch writer will be applied to the Datastore in one RPC call. */ interface DatastoreBatchWriter extends DatastoreWriter { - interface Response { - List generatedKeys(); - } - /** * {@inheritDoc} * This operation will be converted to {@link #put} operation for entities that were already diff --git a/src/main/java/com/google/gcloud/datastore/Transaction.java b/src/main/java/com/google/gcloud/datastore/Transaction.java index 099248465587..543d82b2d75b 100644 --- a/src/main/java/com/google/gcloud/datastore/Transaction.java +++ b/src/main/java/com/google/gcloud/datastore/Transaction.java @@ -17,6 +17,7 @@ package com.google.gcloud.datastore; import java.util.Iterator; +import java.util.List; /** * A Google cloud datastore transaction. @@ -51,7 +52,8 @@ */ public interface Transaction extends DatastoreBatchWriter, DatastoreReaderWriter { - interface Response extends DatastoreBatchWriter.Response { + interface Response { + List generatedKeys(); } /** diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java index 86f5a8908a22..bb19e25a2b8f 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java @@ -16,32 +16,47 @@ package com.google.gcloud.datastore; -import static com.google.gcloud.datastore.DatastoreServiceException.throwInvalidRequest; - import com.google.api.services.datastore.DatastoreV1; +import com.google.common.base.Function; import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.google.gcloud.datastore.TransactionOption.ForceWrites; import com.google.gcloud.datastore.TransactionOption.IsolationLevel; import com.google.protobuf.ByteString; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; -final class TransactionImpl extends BatchImpl implements Transaction { +final class TransactionImpl extends BaseDatastoreBatchWriter implements Transaction { + private final DatastoreServiceImpl datastore; private final ByteString transaction; + private final boolean force; private boolean rolledback; - static class ResponseImpl extends BatchImpl.ResponseImpl implements Transaction.Response { + static class ResponseImpl implements Transaction.Response { + + private final DatastoreV1.CommitResponse response; public ResponseImpl(DatastoreV1.CommitResponse response) { - super(response); + this.response = response; + } + + @Override + public List generatedKeys() { + return Lists.transform(response.getMutationResult().getInsertAutoIdKeyList(), + new Function() { + @Override public Key apply(DatastoreV1.Key keyPb) { + return Key.fromPb(keyPb); + } + }); } } TransactionImpl(DatastoreServiceImpl datastore, TransactionOption... options) { - super(datastore, getBatchOptions(options)); + super("transaction"); + this.datastore = datastore; DatastoreV1.BeginTransactionRequest.Builder requestPb = DatastoreV1.BeginTransactionRequest.newBuilder(); Map, TransactionOption> optionsMap = @@ -50,20 +65,11 @@ public ResponseImpl(DatastoreV1.CommitResponse response) { if (isolationLevel != null) { requestPb.setIsolationLevel(isolationLevel.level().toPb()); } + ForceWrites forceWrites = (ForceWrites) optionsMap.get(TransactionOption.ForceWrites.class); + force = forceWrites == null ? false : forceWrites.force(); transaction = datastore.requestTransactionId(requestPb); } - private static BatchOption[] getBatchOptions(TransactionOption... options) { - List batchOptions = new ArrayList<>(options.length); - for (TransactionOption option : options) { - BatchOption batchOption = option.toBatchWriteOption(); - if (batchOption != null) { - batchOptions.add(batchOption); - } - } - return batchOptions.toArray(new BatchOption[batchOptions.size()]); - } - @Override public Entity get(Key key) { return Iterators.getNext(get(new Key[] {key}), null); @@ -87,41 +93,28 @@ public QueryResult run(Query query) { @Override public Transaction.Response commit() { - return new ResponseImpl(commitRequest()); - } - - @Override - public void rollback() { - super.validateActive(); - if (!rolledback) { - datastore.rollbackTransaction(transaction); + validateActive(); + DatastoreV1.Mutation.Builder mutationPb = toMutationPb(); + if (force) { + mutationPb.setForce(force); } - rolledback = true; - } - - @Override - public boolean active() { - return super.active() && !rolledback; - } - - @Override - protected String getName() { - return "transaction"; + DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder(); + requestPb.setMode(DatastoreV1.CommitRequest.Mode.TRANSACTIONAL); + requestPb.setTransaction(transaction); + requestPb.setMutation(mutationPb); + DatastoreV1.CommitResponse responsePb = datastore.commit(requestPb.build()); + deactivate(); + return new ResponseImpl(responsePb); } @Override - protected void validateActive() { - super.validateActive(); + public void rollback() { if (rolledback) { - throw throwInvalidRequest(getName() + " is not active (was rolledback)"); + return; } - } - - @Override - protected DatastoreV1.CommitRequest.Builder newCommitRequest() { - DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder(); - requestPb.setMode(DatastoreV1.CommitRequest.Mode.TRANSACTIONAL); - requestPb.setTransaction(transaction); - return requestPb; + validateActive(); + datastore.rollbackTransaction(transaction); + deactivate(); + rolledback = true; } } diff --git a/src/main/java/com/google/gcloud/storage/Bucket.java b/src/main/java/com/google/gcloud/storage/Bucket.java index 5fcd7f4abe50..86942b1779e2 100644 --- a/src/main/java/com/google/gcloud/storage/Bucket.java +++ b/src/main/java/com/google/gcloud/storage/Bucket.java @@ -16,7 +16,7 @@ package com.google.gcloud.storage; -import java.nio.ByteBuffer; +import java.util.Iterator; public interface Bucket { @@ -24,33 +24,39 @@ public interface Bucket { String name(); + Cors cors(); + Acl acl(); Acl defaultObjectAcl(); - Cors cors(); + void updateCors(Cors cors); - void updateDefaultObjectAcl(); void updateAcl(Acl acl); + void updateDefaultObjectAcl(); + void delete(String... objectName); + void compose(Iterable sourceObjectNames, String destObjectName); + void copy(String sourceObjectName, StorageObject.Key destObjectKey); - void delete(Key... objectKey); - - void compose(Iterable source, String dest); - - void copy(String source, String dest); - // TODO (ozarov): consider replace with Object that has a reference to bucket and name // that object can return its own meta-data, update its own meta-data, replace its content // via a stream or byteBuffer, read its content (via stream or ByteBuffer),... //void copy(String source, String bucket, String dest); // Also consider read with an offset (and limit). - void put(String name, ByteBuffer bytes); + // returns null if not exists + StorageObject get(String objectName); + + Iterator get(String... objectName); + + InputChannel getInputChannel(String ObjectName); + + OutputChannel getOutputChannel(String ObjectName); // TODO: add listing } diff --git a/src/main/java/com/google/gcloud/storage/Key.java b/src/main/java/com/google/gcloud/storage/InputChannel.java similarity index 57% rename from src/main/java/com/google/gcloud/storage/Key.java rename to src/main/java/com/google/gcloud/storage/InputChannel.java index 1e16d985c021..901ccf048ee9 100644 --- a/src/main/java/com/google/gcloud/storage/Key.java +++ b/src/main/java/com/google/gcloud/storage/InputChannel.java @@ -16,27 +16,22 @@ package com.google.gcloud.storage; -public class Key { +import java.io.Closeable; +import java.io.Serializable; +import java.nio.channels.ReadableByteChannel; - // TODO: add builder, factory method, toURL, from URL, equals,hashCode, toString - private final String bucket; - private final String name; - - /* - Builder() { - - }*/ +/** + * A readable byte channel for reading data from Google Cloud Storage. + * + * Implementations of this class may buffer data internally to reduce remote calls. + * + * This class is @{link Serializable}, which allows incremental reads. + */ +public interface InputChannel extends ReadableByteChannel, Serializable, Closeable { - Key(String bucket, String name) { - this.bucket = bucket; - this.name = name; - } + StorageObject.Key key(); - public String bucket() { - return bucket; - } + int size(); - public String name() { - return name; - } + void seek(int position); } diff --git a/src/main/java/com/google/gcloud/storage/OutputChannel.java b/src/main/java/com/google/gcloud/storage/OutputChannel.java new file mode 100644 index 000000000000..cf45e292ce2c --- /dev/null +++ b/src/main/java/com/google/gcloud/storage/OutputChannel.java @@ -0,0 +1,33 @@ +/* + * 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.storage; + +import java.io.Closeable; +import java.io.Serializable; +import java.nio.channels.WritableByteChannel; + +/** + * A writable byte channel for writing data to Google Cloud Storage. + * + * Implementations of this class may further buffer data internally to reduce remote calls. + * Written data will only be visible after calling {@link #close()}. + * This class is serializable, to allow incremental writes. + */ +public interface OutputChannel extends WritableByteChannel, Serializable, Closeable { + + StorageObject.Key key(); +} diff --git a/src/main/java/com/google/gcloud/storage/StorageObject.java b/src/main/java/com/google/gcloud/storage/StorageObject.java index 0bbdd3b20cc1..2a8dcac2f916 100644 --- a/src/main/java/com/google/gcloud/storage/StorageObject.java +++ b/src/main/java/com/google/gcloud/storage/StorageObject.java @@ -18,9 +18,65 @@ import java.nio.ByteBuffer; +// TODO: add equals,hashCode, toString, serializable public interface StorageObject { - // builder will have an option to populate content and set acl, bucket, name,.. + class Key { + + // TODO: add builder, factory method, toURL, from URL, equals,hashCode, toString, serializable + private final String bucket; + private final String name; + + + Key(Bucket bucket, String name) { + this.bucket = bucket.name(); + this.name = name; + } + + public String bucket() { + return bucket; + } + + public String name() { + return name; + } + } + + abstract class Builder { + + private Bucket bucket; + private Acl acl; + private String name; + private ByteBuffer content; + + public Builder() { + + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder bucket(Bucket bucket) { + this.bucket = bucket; + return this; + } + + public Builder acl(Acl acl) { + this.acl = acl; + return this; + } + + public Builder content(ByteBuffer content) { + this.content = content; + return this; + } + + public abstract StorageObject build(); + } + + boolean exists(); Key key(); @@ -28,4 +84,11 @@ public interface StorageObject { ByteBuffer content(); + void save(); + + void delete(); + + InputChannel getInputChannel(); + + OutputChannel getOutputChannel(); } diff --git a/src/main/java/com/google/gcloud/storage/StorageService.java b/src/main/java/com/google/gcloud/storage/StorageService.java index 55b9b84c1e2b..313a01f82258 100644 --- a/src/main/java/com/google/gcloud/storage/StorageService.java +++ b/src/main/java/com/google/gcloud/storage/StorageService.java @@ -20,5 +20,5 @@ public interface StorageService { Iterable listBuckets(); - Bucket getBucket(String bucket); + Bucket getBucket(String bucketName); } diff --git a/src/site/apt/index.apt b/src/site/apt/index.apt new file mode 100644 index 000000000000..01347874fd16 --- /dev/null +++ b/src/site/apt/index.apt @@ -0,0 +1,23 @@ +GCloud Java: Idiomatic Java Client for Google Cloud Platform services. + + This is a Java Client for accessing Google Cloud Platorm services such as Datastore, Storage, PubSub and others. + This library is in a early stage of its development and may occasionally make backwards-incompatible changes, + but it is already usable. + +* Features + + * {{{https://cloud.google.com/datastore/}Google Cloud Datastore}} + +* Links + + * {{{https://github.com/aozarov/git-demo}GitHub repository}} + + * {{{http://aozarov.github.io/git-demo/apidocs/index.html}Javadocs}} + + * {{{http://aozarov.github.io/git-demo/dependencies.html}Dependencies}} + + * {{{https://travis-ci.org/aozarov/git-demo} Continous Integration System (Travis-CI)}} + + * {{{https://github.com/aozarov/git-demo/issues?page=1&state=open}Issues}} + + * {{{https://coveralls.io/r/aozarov/git-demo?branch=master}Coverage}} diff --git a/src/site/site.xml b/src/site/site.xml new file mode 100644 index 000000000000..afe6c29f6042 --- /dev/null +++ b/src/site/site.xml @@ -0,0 +1,11 @@ + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + + + diff --git a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java similarity index 98% rename from src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java rename to src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java index 8bc31d5381c3..10537734ef16 100644 --- a/src/test/java/com/google/gcloud/datastore/DatastoreServiceIntegrationTest.java +++ b/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java @@ -29,9 +29,9 @@ import com.google.gcloud.datastore.StructuredQuery.OrderBy; import com.google.gcloud.datastore.StructuredQuery.Projection; import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; - -import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -42,7 +42,7 @@ import java.util.List; @RunWith(JUnit4.class) -public class DatastoreServiceIntegrationTest { +public class DatastoreServiceTest { private static final String DATASET = LocalGcdHelper.DEFAULT_DATASET; private static final String KIND1 = "kind1"; @@ -86,13 +86,18 @@ public class DatastoreServiceIntegrationTest { private DatastoreServiceOptions options; private DatastoreService datastore; private DatastoreHelper helper; - private LocalGcdHelper gcdHelper; - @Before - public void setUp() throws IOException, InterruptedException { + private static LocalGcdHelper gcdHelper; + + @BeforeClass + public static void beforeClass() throws IOException, InterruptedException { if (!LocalGcdHelper.isActive(DATASET)) { gcdHelper = LocalGcdHelper.start(DATASET); } + } + + @Before + public void setUp() throws IOException, InterruptedException { options = DatastoreServiceOptions.builder() .dataset(DATASET) .host("http://localhost:" + LocalGcdHelper.PORT) @@ -104,8 +109,8 @@ public void setUp() throws IOException, InterruptedException { datastore.add(ENTITY1, ENTITY2); } - @After - public void tearDown() throws IOException, InterruptedException { + @AfterClass + public static void afterClass() throws IOException, InterruptedException { if (gcdHelper != null) { gcdHelper.stop(); } From dd656b6ed809b0e16c4669cd5ae92f481c30f591 Mon Sep 17 00:00:00 2001 From: aozarov Date: Mon, 9 Feb 2015 16:10:11 -0800 Subject: [PATCH 100/771] return the datastore service from Batch and Transation --- src/main/java/com/google/gcloud/datastore/Batch.java | 5 +++++ src/main/java/com/google/gcloud/datastore/BatchImpl.java | 5 +++++ src/main/java/com/google/gcloud/datastore/Transaction.java | 5 +++++ .../java/com/google/gcloud/datastore/TransactionImpl.java | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/src/main/java/com/google/gcloud/datastore/Batch.java b/src/main/java/com/google/gcloud/datastore/Batch.java index a6ba78160ef4..f74ccc288808 100644 --- a/src/main/java/com/google/gcloud/datastore/Batch.java +++ b/src/main/java/com/google/gcloud/datastore/Batch.java @@ -46,4 +46,9 @@ interface Response { * @throws DatastoreServiceException if there was any failure or if batch is not longer active */ Response submit(); + + /** + * Returns the batch associated {@link DatastoreService}. + */ + DatastoreService datastore(); } diff --git a/src/main/java/com/google/gcloud/datastore/BatchImpl.java b/src/main/java/com/google/gcloud/datastore/BatchImpl.java index 7139ca3bb1c6..305bf4fbcf9f 100644 --- a/src/main/java/com/google/gcloud/datastore/BatchImpl.java +++ b/src/main/java/com/google/gcloud/datastore/BatchImpl.java @@ -72,4 +72,9 @@ public Batch.Response submit() { deactivate(); return new ResponseImpl(responsePb); } + + @Override + public DatastoreService datastore() { + return datastore; + } } diff --git a/src/main/java/com/google/gcloud/datastore/Transaction.java b/src/main/java/com/google/gcloud/datastore/Transaction.java index 543d82b2d75b..28592552787b 100644 --- a/src/main/java/com/google/gcloud/datastore/Transaction.java +++ b/src/main/java/com/google/gcloud/datastore/Transaction.java @@ -109,4 +109,9 @@ interface Response { */ @Override boolean active(); + + /** + * Returns the transaction associated {@link DatastoreService}. + */ + DatastoreService datastore(); } diff --git a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java index bb19e25a2b8f..859f637d4c72 100644 --- a/src/main/java/com/google/gcloud/datastore/TransactionImpl.java +++ b/src/main/java/com/google/gcloud/datastore/TransactionImpl.java @@ -117,4 +117,9 @@ public void rollback() { deactivate(); rolledback = true; } + + @Override + public DatastoreService datastore() { + return datastore; + } } From d7a50f43bf96c1b9d2d4209acb62cf6a89bfd93a Mon Sep 17 00:00:00 2001 From: Arie Date: Mon, 9 Feb 2015 16:19:03 -0800 Subject: [PATCH 101/771] Delete .project --- .project | 65 -------------------------------------------------------- 1 file changed, 65 deletions(-) delete mode 100644 .project diff --git a/.project b/.project deleted file mode 100644 index 89b7470af643..000000000000 --- a/.project +++ /dev/null @@ -1,65 +0,0 @@ - - - git-demo - - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - org.eclipse.jdt.core.javabuilder - - - - - net.sf.eclipsecs.core.CheckstyleBuilder - - - - - edu.umd.cs.findbugs.plugin.eclipse.findbugsBuilder - - - - - edu.umd.cs.findbugs.plugin.eclipse.findbugsBuilder - - - - - org.eclipse.wst.validation.validationbuilder - - - - - ntut.csie.rleht.builder.RLBuilder - - - - - ch.acanda.eclipse.pmd.builder.PMDBuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jem.workbench.JavaEMFNature - org.eclipse.wst.common.modulecore.ModuleCoreNature - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - net.sf.eclipsecs.core.CheckstyleNature - edu.umd.cs.findbugs.plugin.eclipse.findbugsNature - org.eclipse.wst.common.project.facet.core.nature - ntut.csie.rleht.builder.RLNature - ch.acanda.eclipse.pmd.builder.PMDNature - - From 3048007abede192582b1ffdf36779306f42ed60a Mon Sep 17 00:00:00 2001 From: Arie Date: Mon, 9 Feb 2015 16:19:10 -0800 Subject: [PATCH 102/771] Delete .classpath --- .classpath | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 .classpath diff --git a/.classpath b/.classpath deleted file mode 100644 index dc5384546f80..000000000000 --- a/.classpath +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From a87985ee4967bcf6436a85edc4be3420d3468f60 Mon Sep 17 00:00:00 2001 From: Arie Date: Mon, 9 Feb 2015 16:31:29 -0800 Subject: [PATCH 103/771] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e9c47ebd268..7bbb1da1f68d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Google Cloud for Java ===================== [![Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/gcloud-java) -[![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/gcloud-java/badge.svg)](https://coveralls.io/r/GoogleCloudPlatform/gcloud-java) +[![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/gcloud-java/badge.svg?branch=master)](https://coveralls.io/r/GoogleCloudPlatform/gcloud-java?branch=master) Java idiomatic client for Google Cloud Platform services. Supported APIs include: From a2f9386e65674984f0889b4e3d2331842a03d8a7 Mon Sep 17 00:00:00 2001 From: Arie Date: Mon, 9 Feb 2015 16:35:50 -0800 Subject: [PATCH 104/771] Update index.apt --- src/site/apt/index.apt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/site/apt/index.apt b/src/site/apt/index.apt index 01347874fd16..98928898bbd3 100644 --- a/src/site/apt/index.apt +++ b/src/site/apt/index.apt @@ -10,14 +10,14 @@ GCloud Java: Idiomatic Java Client for Google Cloud Platform services. * Links - * {{{https://github.com/aozarov/git-demo}GitHub repository}} + * {{{https://github.com/GoogleCloudPlatform/gcloud-java}GitHub repository}} - * {{{http://aozarov.github.io/git-demo/apidocs/index.html}Javadocs}} + * {{{http://GoogleCloudPlatform.github.io/gcloud-java/apidocs/index.html}Javadocs}} - * {{{http://aozarov.github.io/git-demo/dependencies.html}Dependencies}} + * {{{http://GoogleCloudPlatform.github.io/gcloud-java/dependencies.html}Dependencies}} - * {{{https://travis-ci.org/aozarov/git-demo} Continous Integration System (Travis-CI)}} + * {{{https://travis-ci.org/GoogleCloudPlatform/gcloud-java} Continous Integration System (Travis-CI)}} - * {{{https://github.com/aozarov/git-demo/issues?page=1&state=open}Issues}} + * {{{https://github.com/GoogleCloudPlatform/gcloud-java/issues?page=1&state=open}Issues}} - * {{{https://coveralls.io/r/aozarov/git-demo?branch=master}Coverage}} + * {{{https://coveralls.io/r/GoogleCloudPlatform/gcloud-java/issues?branch=master}Coverage}} From 6a3354a5478aa31b98241325976b79af9a84ce7e Mon Sep 17 00:00:00 2001 From: Arie Date: Mon, 9 Feb 2015 16:53:11 -0800 Subject: [PATCH 105/771] Update index.apt --- src/site/apt/index.apt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/apt/index.apt b/src/site/apt/index.apt index 98928898bbd3..0d4999bb7764 100644 --- a/src/site/apt/index.apt +++ b/src/site/apt/index.apt @@ -20,4 +20,4 @@ GCloud Java: Idiomatic Java Client for Google Cloud Platform services. * {{{https://github.com/GoogleCloudPlatform/gcloud-java/issues?page=1&state=open}Issues}} - * {{{https://coveralls.io/r/GoogleCloudPlatform/gcloud-java/issues?branch=master}Coverage}} + * {{{https://coveralls.io/r/GoogleCloudPlatform/gcloud-java/}Coverage}} From fe95d030979b92207a052c815b1aee3ae24d6e6b Mon Sep 17 00:00:00 2001 From: Arie Date: Mon, 9 Feb 2015 16:54:09 -0800 Subject: [PATCH 106/771] Update site.xml --- src/site/site.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/site/site.xml b/src/site/site.xml index afe6c29f6042..f47cc45a401f 100644 --- a/src/site/site.xm