From bb99a20ade97963b6086f834e70ed4a4c5627873 Mon Sep 17 00:00:00 2001 From: garrettjonesgoogle Date: Fri, 9 Dec 2016 10:08:58 -0800 Subject: [PATCH 01/22] Update version to 0.8.1-SNAPSHOT (#1467) Also, update versions in README to 0.8.0 --- README.md | 6 +++--- google-cloud-bigquery/README.md | 6 +++--- google-cloud-bigquery/pom.xml | 2 +- google-cloud-compute/README.md | 6 +++--- google-cloud-compute/pom.xml | 2 +- google-cloud-contrib/README.md | 6 +++--- google-cloud-contrib/google-cloud-nio-examples/pom.xml | 2 +- google-cloud-contrib/google-cloud-nio/README.md | 6 +++--- google-cloud-contrib/google-cloud-nio/pom.xml | 2 +- google-cloud-contrib/pom.xml | 2 +- google-cloud-core/README.md | 6 +++--- google-cloud-core/pom.xml | 2 +- google-cloud-datastore/README.md | 6 +++--- google-cloud-datastore/pom.xml | 2 +- google-cloud-dns/README.md | 6 +++--- google-cloud-dns/pom.xml | 2 +- google-cloud-errorreporting/pom.xml | 2 +- google-cloud-examples/README.md | 6 +++--- google-cloud-examples/pom.xml | 2 +- google-cloud-language/pom.xml | 2 +- google-cloud-logging/README.md | 6 +++--- google-cloud-logging/pom.xml | 2 +- google-cloud-monitoring/pom.xml | 2 +- google-cloud-pubsub/README.md | 6 +++--- google-cloud-pubsub/pom.xml | 2 +- google-cloud-resourcemanager/README.md | 6 +++--- google-cloud-resourcemanager/pom.xml | 2 +- google-cloud-speech/pom.xml | 2 +- google-cloud-storage/README.md | 6 +++--- google-cloud-storage/pom.xml | 2 +- google-cloud-trace/pom.xml | 2 +- google-cloud-translate/README.md | 6 +++--- google-cloud-translate/pom.xml | 2 +- google-cloud-vision/pom.xml | 2 +- google-cloud/README.md | 6 +++--- google-cloud/pom.xml | 2 +- pom.xml | 6 +++--- 37 files changed, 69 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 190d900c044b..a648c11f5e32 100644 --- a/README.md +++ b/README.md @@ -43,16 +43,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud - 0.7.0 + 0.8.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud:0.7.0' +compile 'com.google.cloud:google-cloud:0.8.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud" % "0.8.0" ``` Example Applications diff --git a/google-cloud-bigquery/README.md b/google-cloud-bigquery/README.md index 19db3002288f..616494887e3b 100644 --- a/google-cloud-bigquery/README.md +++ b/google-cloud-bigquery/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-bigquery - 0.7.0 + 0.8.0-beta ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-bigquery:0.7.0' +compile 'com.google.cloud:google-cloud-bigquery:0.8.0-beta' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-bigquery" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud-bigquery" % "0.8.0-beta" ``` Example Application diff --git a/google-cloud-bigquery/pom.xml b/google-cloud-bigquery/pom.xml index 542d7e9b734f..73383afce15f 100644 --- a/google-cloud-bigquery/pom.xml +++ b/google-cloud-bigquery/pom.xml @@ -12,7 +12,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-bigquery diff --git a/google-cloud-compute/README.md b/google-cloud-compute/README.md index 861bb539efbb..f7c18a0eafa9 100644 --- a/google-cloud-compute/README.md +++ b/google-cloud-compute/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-compute - 0.7.0 + 0.8.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-compute:0.7.0' +compile 'com.google.cloud:google-cloud-compute:0.8.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-compute" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud-compute" % "0.8.0" ``` Example Application diff --git a/google-cloud-compute/pom.xml b/google-cloud-compute/pom.xml index ec147782323e..a9203ad718ac 100644 --- a/google-cloud-compute/pom.xml +++ b/google-cloud-compute/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-compute diff --git a/google-cloud-contrib/README.md b/google-cloud-contrib/README.md index 2b27b0e06da6..8b7aa1323089 100644 --- a/google-cloud-contrib/README.md +++ b/google-cloud-contrib/README.md @@ -25,16 +25,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-contrib - 0.7.0 + 0.8.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-contrib:0.7.0' +compile 'com.google.cloud:google-cloud-contrib:0.8.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-contrib" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud-contrib" % "0.8.0" ``` ### google-cloud-nio-examples diff --git a/google-cloud-contrib/google-cloud-nio-examples/pom.xml b/google-cloud-contrib/google-cloud-nio-examples/pom.xml index 8b26a655f9d4..2b07df873af8 100644 --- a/google-cloud-contrib/google-cloud-nio-examples/pom.xml +++ b/google-cloud-contrib/google-cloud-nio-examples/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-contrib - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-nio-examples diff --git a/google-cloud-contrib/google-cloud-nio/README.md b/google-cloud-contrib/google-cloud-nio/README.md index e57e8a207ed7..7d7df3b722bc 100644 --- a/google-cloud-contrib/google-cloud-nio/README.md +++ b/google-cloud-contrib/google-cloud-nio/README.md @@ -26,16 +26,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-nio - 0.7.0 + 0.8.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-nio:0.7.0' +compile 'com.google.cloud:google-cloud-nio:0.8.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-nio" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud-nio" % "0.8.0" ``` Example Applications diff --git a/google-cloud-contrib/google-cloud-nio/pom.xml b/google-cloud-contrib/google-cloud-nio/pom.xml index 18a7cfd954bc..0646be3fe3da 100644 --- a/google-cloud-contrib/google-cloud-nio/pom.xml +++ b/google-cloud-contrib/google-cloud-nio/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-contrib - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-nio diff --git a/google-cloud-contrib/pom.xml b/google-cloud-contrib/pom.xml index 98fc4ddeb6e7..81f42e03b96d 100644 --- a/google-cloud-contrib/pom.xml +++ b/google-cloud-contrib/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-contrib diff --git a/google-cloud-core/README.md b/google-cloud-core/README.md index a248cc79f1a7..f711365e184b 100644 --- a/google-cloud-core/README.md +++ b/google-cloud-core/README.md @@ -19,16 +19,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-core - 0.7.0 + 0.8.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-core:0.7.0' +compile 'com.google.cloud:google-cloud-core:0.8.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-core" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud-core" % "0.8.0" ``` Troubleshooting diff --git a/google-cloud-core/pom.xml b/google-cloud-core/pom.xml index 34614cdf1b49..489d27cdf126 100644 --- a/google-cloud-core/pom.xml +++ b/google-cloud-core/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-core diff --git a/google-cloud-datastore/README.md b/google-cloud-datastore/README.md index 907d3c8f6e9b..db84b8db9ad1 100644 --- a/google-cloud-datastore/README.md +++ b/google-cloud-datastore/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-datastore - 0.7.0 + 0.8.0-beta ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-datastore:0.7.0' +compile 'com.google.cloud:google-cloud-datastore:0.8.0-beta' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-datastore" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud-datastore" % "0.8.0-beta" ``` Example Application diff --git a/google-cloud-datastore/pom.xml b/google-cloud-datastore/pom.xml index d131a94b9e0a..100ad5edcfd9 100644 --- a/google-cloud-datastore/pom.xml +++ b/google-cloud-datastore/pom.xml @@ -12,7 +12,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-datastore diff --git a/google-cloud-dns/README.md b/google-cloud-dns/README.md index 1380e2466a0f..6ae180f976dd 100644 --- a/google-cloud-dns/README.md +++ b/google-cloud-dns/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-dns - 0.7.0 + 0.8.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-dns:0.7.0' +compile 'com.google.cloud:google-cloud-dns:0.8.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-dns" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud-dns" % "0.8.0" ``` Example Application diff --git a/google-cloud-dns/pom.xml b/google-cloud-dns/pom.xml index 771b82032136..a53283c9f453 100644 --- a/google-cloud-dns/pom.xml +++ b/google-cloud-dns/pom.xml @@ -13,7 +13,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-dns diff --git a/google-cloud-errorreporting/pom.xml b/google-cloud-errorreporting/pom.xml index 6d40d28c1915..dcff54850fc9 100644 --- a/google-cloud-errorreporting/pom.xml +++ b/google-cloud-errorreporting/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-errorreporting diff --git a/google-cloud-examples/README.md b/google-cloud-examples/README.md index 7e9b48378a17..5dbbffcb9f5e 100644 --- a/google-cloud-examples/README.md +++ b/google-cloud-examples/README.md @@ -19,16 +19,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-examples - 0.7.0 + 0.8.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-examples:0.7.0' +compile 'com.google.cloud:google-cloud-examples:0.8.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-examples" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud-examples" % "0.8.0" ``` To run examples from your command line: diff --git a/google-cloud-examples/pom.xml b/google-cloud-examples/pom.xml index d44a7e0eb39a..ffbb5f46df5f 100644 --- a/google-cloud-examples/pom.xml +++ b/google-cloud-examples/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-examples diff --git a/google-cloud-language/pom.xml b/google-cloud-language/pom.xml index ea19f65cd3f8..2d8c6ed025ad 100644 --- a/google-cloud-language/pom.xml +++ b/google-cloud-language/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-language diff --git a/google-cloud-logging/README.md b/google-cloud-logging/README.md index 6d0fed15e28e..e439890b3b2c 100644 --- a/google-cloud-logging/README.md +++ b/google-cloud-logging/README.md @@ -26,16 +26,16 @@ Add this to your pom.xml file com.google.cloud google-cloud-logging - 0.7.0 + 0.8.0-beta ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-logging:0.7.0' +compile 'com.google.cloud:google-cloud-logging:0.8.0-beta' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-logging" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud-logging" % "0.8.0-beta" ``` Example Application diff --git a/google-cloud-logging/pom.xml b/google-cloud-logging/pom.xml index 2994b1c10e6d..9c5e73155481 100644 --- a/google-cloud-logging/pom.xml +++ b/google-cloud-logging/pom.xml @@ -12,7 +12,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-logging diff --git a/google-cloud-monitoring/pom.xml b/google-cloud-monitoring/pom.xml index 28e282e72f61..be83a529ed1b 100644 --- a/google-cloud-monitoring/pom.xml +++ b/google-cloud-monitoring/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-monitoring diff --git a/google-cloud-pubsub/README.md b/google-cloud-pubsub/README.md index 7d426b7d5f49..dc60848f7bed 100644 --- a/google-cloud-pubsub/README.md +++ b/google-cloud-pubsub/README.md @@ -26,16 +26,16 @@ Add this to your pom.xml file com.google.cloud google-cloud-pubsub - 0.7.0 + 0.8.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-pubsub:0.7.0' +compile 'com.google.cloud:google-cloud-pubsub:0.8.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-pubsub" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud-pubsub" % "0.8.0" ``` Example Application diff --git a/google-cloud-pubsub/pom.xml b/google-cloud-pubsub/pom.xml index 8b25ba971cc7..3d268c6771da 100644 --- a/google-cloud-pubsub/pom.xml +++ b/google-cloud-pubsub/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-pubsub diff --git a/google-cloud-resourcemanager/README.md b/google-cloud-resourcemanager/README.md index f7398bf84ac1..e261feb07158 100644 --- a/google-cloud-resourcemanager/README.md +++ b/google-cloud-resourcemanager/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-resourcemanager - 0.7.0 + 0.8.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-resourcemanager:0.7.0' +compile 'com.google.cloud:google-cloud-resourcemanager:0.8.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-resourcemanager" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud-resourcemanager" % "0.8.0" ``` Example Application diff --git a/google-cloud-resourcemanager/pom.xml b/google-cloud-resourcemanager/pom.xml index 4d305d1d921b..cc0da8a2cb30 100644 --- a/google-cloud-resourcemanager/pom.xml +++ b/google-cloud-resourcemanager/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-resourcemanager diff --git a/google-cloud-speech/pom.xml b/google-cloud-speech/pom.xml index 6bd7d2695609..52fd58a97b58 100644 --- a/google-cloud-speech/pom.xml +++ b/google-cloud-speech/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-speech diff --git a/google-cloud-storage/README.md b/google-cloud-storage/README.md index 069c486f6f52..d415267d7a0e 100644 --- a/google-cloud-storage/README.md +++ b/google-cloud-storage/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-storage - 0.7.0 + 0.8.0-beta ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-storage:0.7.0' +compile 'com.google.cloud:google-cloud-storage:0.8.0-beta' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-storage" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud-storage" % "0.8.0-beta" ``` Example Application diff --git a/google-cloud-storage/pom.xml b/google-cloud-storage/pom.xml index 2fd3f122e278..1b7c8e86be6b 100644 --- a/google-cloud-storage/pom.xml +++ b/google-cloud-storage/pom.xml @@ -12,7 +12,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-storage diff --git a/google-cloud-trace/pom.xml b/google-cloud-trace/pom.xml index 35b9a037fba6..f787b6c3924b 100644 --- a/google-cloud-trace/pom.xml +++ b/google-cloud-trace/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-trace diff --git a/google-cloud-translate/README.md b/google-cloud-translate/README.md index 8f9ac91d2f8e..5571619bb0c2 100644 --- a/google-cloud-translate/README.md +++ b/google-cloud-translate/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-translate - 0.7.0 + 0.8.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-translate:0.7.0' +compile 'com.google.cloud:google-cloud-translate:0.8.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-translate" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud-translate" % "0.8.0" ``` Example Application diff --git a/google-cloud-translate/pom.xml b/google-cloud-translate/pom.xml index 67c3b18c7a08..8d645b08643e 100644 --- a/google-cloud-translate/pom.xml +++ b/google-cloud-translate/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-translate diff --git a/google-cloud-vision/pom.xml b/google-cloud-vision/pom.xml index 32cbd9b5c062..f14e51c7b2e8 100644 --- a/google-cloud-vision/pom.xml +++ b/google-cloud-vision/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT google-cloud-vision diff --git a/google-cloud/README.md b/google-cloud/README.md index f17c99715895..9da900cd8b2a 100644 --- a/google-cloud/README.md +++ b/google-cloud/README.md @@ -27,16 +27,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud - 0.7.0 + 0.8.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud:0.7.0' +compile 'com.google.cloud:google-cloud:0.8.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud" % "0.7.0" +libraryDependencies += "com.google.cloud" % "google-cloud" % "0.8.0" ``` Troubleshooting diff --git a/google-cloud/pom.xml b/google-cloud/pom.xml index edd11e504cd9..a162e2e61f7f 100644 --- a/google-cloud/pom.xml +++ b/google-cloud/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.0 + 0.8.1-SNAPSHOT diff --git a/pom.xml b/pom.xml index ce2d365e9958..8534ae252605 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-pom pom - 0.8.0 + 0.8.1-SNAPSHOT Google Cloud https://github.com/GoogleCloudPlatform/google-cloud-java @@ -92,8 +92,8 @@ github 0.6.0 1.0.1 - 0.8.0 - 0.8.0-beta + 0.8.1-SNAPSHOT + 0.8.1-beta-SNAPSHOT ${beta.version} google-cloud From b85e1cccc67f254cd7bcfe3d9bc5458a039482ff Mon Sep 17 00:00:00 2001 From: Tim Swast Date: Fri, 9 Dec 2016 11:05:28 -0800 Subject: [PATCH 02/22] Add link to Maven Central for maven-central badge. (#1468) Used to link to the image, which wasn't super useful. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a648c11f5e32..6d423b25be1f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Java idiomatic client for [Google Cloud Platform][cloud-platform] services. [![Build Status](https://travis-ci.org/GoogleCloudPlatform/google-cloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/google-cloud-java) [![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/google-cloud-java/badge.svg?branch=master)](https://coveralls.io/r/GoogleCloudPlatform/google-cloud-java?branch=master) -[![Maven](https://img.shields.io/maven-central/v/com.google.cloud/google-cloud.svg)]( https://img.shields.io/maven-central/v/com.google.cloud/google-cloud.svg) +[![Maven](https://img.shields.io/maven-central/v/com.google.cloud/google-cloud.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.google.cloud%22%20a%3A%22google-cloud%22) [![Codacy Badge](https://api.codacy.com/project/badge/grade/9da006ad7c3a4fe1abd142e77c003917)](https://www.codacy.com/app/mziccard/google-cloud-java) [![Dependency Status](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969/badge.svg?style=flat)](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969) From 9a308a209f083774c07926c535a54a31f8e054a3 Mon Sep 17 00:00:00 2001 From: Michael Darakananda Date: Mon, 12 Dec 2016 17:34:52 +1100 Subject: [PATCH 03/22] fix more races in pubsub tests Previously BlockingProcessStreamReader has a terminate() method, used to tell the Reader to stop reading from the emulator process. This causes an inter-process race. If the Reader stops before reading emulator's output, the emulator process will hang as it tries to write to stdout/stderr as there's no one to read from the other side of the pipe. Since there is no way to safely stop the Reader, this commit deletes the method and its associated test. Additionally, the timeout for LocalSystemTest is increased to 3 minutes, since the emulator, somehow, consistently takes just longer than a minute to shut down. --- .../com/google/cloud/testing/BaseEmulatorHelper.java | 8 ++++---- .../cloud/testing/BlockingProcessStreamReader.java | 8 +------- .../cloud/testing/BlockingProcessStreamReaderTest.java | 9 --------- .../java/com/google/cloud/pubsub/LocalSystemTest.java | 2 +- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/google-cloud-core/src/main/java/com/google/cloud/testing/BaseEmulatorHelper.java b/google-cloud-core/src/main/java/com/google/cloud/testing/BaseEmulatorHelper.java index dee22e596c11..2b4276068d7b 100644 --- a/google-cloud-core/src/main/java/com/google/cloud/testing/BaseEmulatorHelper.java +++ b/google-cloud-core/src/main/java/com/google/cloud/testing/BaseEmulatorHelper.java @@ -114,15 +114,15 @@ protected final void startProcess(String blockUntilOutput) * and stop any possible thread listening for its output. */ protected final int waitForProcess(Duration timeout) throws IOException, InterruptedException, TimeoutException { - if (blockingProcessReader != null) { - blockingProcessReader.terminate(); - blockingProcessReader = null; - } if (activeRunner != null) { int exitCode = activeRunner.waitFor(timeout); activeRunner = null; return exitCode; } + if (blockingProcessReader != null) { + blockingProcessReader.join(); + blockingProcessReader = null; + } return 0; } diff --git a/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java b/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java index 0fa10dcacebb..f4e69b9b6e19 100644 --- a/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java +++ b/google-cloud-core/src/main/java/com/google/cloud/testing/BlockingProcessStreamReader.java @@ -61,10 +61,6 @@ private BlockingProcessStreamReader(String emulator, InputStream stream, String } } - void terminate() throws IOException { - interrupt(); - } - @Override public void run() { String previousLine = ""; @@ -79,9 +75,7 @@ public void run() { processLogLine(previousLine, nextLine); } } catch (IOException e) { - if (!isInterrupted()) { - e.printStackTrace(System.err); - } + e.printStackTrace(System.err); } processLogLine(previousLine, firstNonNull(nextLine, "")); writeLog(); diff --git a/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java b/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java index 6dedcb55b680..1b22c9f3ff91 100644 --- a/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java +++ b/google-cloud-core/src/test/java/com/google/cloud/testing/BlockingProcessStreamReaderTest.java @@ -74,15 +74,6 @@ Multimap getLogs() { } } - @Test - public void testBlockUntil() throws IOException { - InputStream stream = new ByteArrayInputStream(OUTPUT.getBytes(Charsets.UTF_8)); - BlockingProcessStreamReader thread = - BlockingProcessStreamReader.start("emulator", stream, BLOCK_UNTIL, null); - thread.terminate(); - stream.close(); - } - @Test public void testForwardLogEntry() throws IOException, InterruptedException { TestLogger logger = new TestLogger(); diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/LocalSystemTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/LocalSystemTest.java index 3edb6342c0cb..aba64436933b 100644 --- a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/LocalSystemTest.java +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/LocalSystemTest.java @@ -50,6 +50,6 @@ public static void startServer() throws IOException, InterruptedException { public static void stopServer() throws Exception { pubsub.close(); pubsubHelper.reset(); - pubsubHelper.stop(Duration.standardMinutes(1)); + pubsubHelper.stop(Duration.standardMinutes(3)); } } From 061194950feb14af37c6c14786c216f2208a1dad Mon Sep 17 00:00:00 2001 From: garrettjonesgoogle Date: Thu, 5 Jan 2017 11:37:06 -0800 Subject: [PATCH 04/22] Regenerating SPI layer (#1501) * Converting Error Reporting and Monitoring to use resource name types * Removing formatX/parseX methods from pubsub, converting usage of the same to resource name types * New methods in Logging and PubSub --- google-cloud-core/pom.xml | 4 +- google-cloud-errorreporting/pom.xml | 2 +- .../spi/v1beta1/ErrorGroupServiceClient.java | 45 +--- .../spi/v1beta1/ErrorStatsServiceClient.java | 92 +++---- .../v1beta1/ReportErrorsServiceClient.java | 40 ++- .../spi/v1beta1/package-info.java | 12 +- .../spi/v1beta1/ErrorGroupServiceTest.java | 21 +- .../spi/v1beta1/ErrorStatsServiceTest.java | 32 +-- .../spi/v1beta1/ReportErrorsServiceTest.java | 11 +- google-cloud-language/pom.xml | 2 +- google-cloud-logging/pom.xml | 2 +- .../logging/spi/v2/ConfigServiceV2Client.java | 62 +++-- .../spi/v2/LoggingServiceV2Client.java | 146 ++++++++++- .../spi/v2/LoggingServiceV2Settings.java | 84 +++++- .../logging/spi/v2/PagedResponseWrappers.java | 14 + .../logging/spi/v2/LoggingServiceV2Test.java | 49 ++++ .../spi/v2/MockLoggingServiceV2Impl.java | 16 ++ google-cloud-monitoring/pom.xml | 2 +- .../monitoring/spi/v3/GroupServiceClient.java | 128 ++++----- .../spi/v3/MetricServiceClient.java | 247 ++++++------------ .../cloud/monitoring/spi/v3/package-info.java | 8 +- .../monitoring/spi/v3/GroupServiceTest.java | 66 ++--- .../monitoring/spi/v3/MetricServiceTest.java | 111 ++++---- google-cloud-pubsub/pom.xml | 2 +- .../cloud/pubsub/MessageConsumerImpl.java | 5 +- .../com/google/cloud/pubsub/PubSubImpl.java | 54 ++-- .../google/cloud/pubsub/SubscriptionId.java | 9 +- .../google/cloud/pubsub/SubscriptionInfo.java | 7 +- .../java/com/google/cloud/pubsub/TopicId.java | 9 +- .../com/google/cloud/pubsub/TopicInfo.java | 7 +- .../cloud/pubsub/spi/v1/PublisherClient.java | 80 ++---- .../cloud/pubsub/spi/v1/SubscriberClient.java | 233 +++++++++-------- .../pubsub/spi/v1/SubscriberSettings.java | 24 ++ .../google/cloud/pubsub/PubSubImplTest.java | 24 +- .../pubsub/spi/v1/MockSubscriberImpl.java | 32 +++ .../cloud/pubsub/spi/v1/PublisherTest.java | 12 +- .../cloud/pubsub/spi/v1/SubscriberTest.java | 84 +++++- google-cloud-speech/pom.xml | 2 +- google-cloud-trace/pom.xml | 2 +- google-cloud-vision/pom.xml | 2 +- 40 files changed, 1022 insertions(+), 762 deletions(-) diff --git a/google-cloud-core/pom.xml b/google-cloud-core/pom.xml index 489d27cdf126..613c7f1e98d5 100644 --- a/google-cloud-core/pom.xml +++ b/google-cloud-core/pom.xml @@ -122,7 +122,7 @@ com.google.api.grpc grpc-google-common-protos - 0.1.3 + 0.1.5 io.grpc @@ -133,7 +133,7 @@ com.google.api.grpc grpc-google-iam-v1 - 0.1.3 + 0.1.5 io.grpc diff --git a/google-cloud-errorreporting/pom.xml b/google-cloud-errorreporting/pom.xml index dcff54850fc9..49cda878a49e 100644 --- a/google-cloud-errorreporting/pom.xml +++ b/google-cloud-errorreporting/pom.xml @@ -30,7 +30,7 @@ com.google.api.grpc grpc-google-cloud-error-reporting-v1beta1 - 0.1.3 + 0.1.5 io.grpc diff --git a/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorGroupServiceClient.java b/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorGroupServiceClient.java index 48b9cc6bba46..33e1edc3d84a 100644 --- a/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorGroupServiceClient.java +++ b/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorGroupServiceClient.java @@ -17,9 +17,9 @@ import com.google.api.gax.grpc.ChannelAndExecutor; import com.google.api.gax.grpc.UnaryCallable; -import com.google.api.gax.protobuf.PathTemplate; import com.google.devtools.clouderrorreporting.v1beta1.ErrorGroup; import com.google.devtools.clouderrorreporting.v1beta1.GetGroupRequest; +import com.google.devtools.clouderrorreporting.v1beta1.GroupName; import com.google.devtools.clouderrorreporting.v1beta1.UpdateGroupRequest; import com.google.protobuf.ExperimentalApi; import io.grpc.ManagedChannel; @@ -40,8 +40,8 @@ *
  * 
  * try (ErrorGroupServiceClient errorGroupServiceClient = ErrorGroupServiceClient.create()) {
- *   String formattedGroupName = ErrorGroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
- *   ErrorGroup response = errorGroupServiceClient.getGroup(formattedGroupName);
+ *   GroupName groupName = GroupName.create("[PROJECT]", "[GROUP]");
+ *   ErrorGroup response = errorGroupServiceClient.getGroup(groupName);
  * }
  * 
  * 
@@ -97,26 +97,6 @@ public class ErrorGroupServiceClient implements AutoCloseable { private final UnaryCallable getGroupCallable; private final UnaryCallable updateGroupCallable; - private static final PathTemplate GROUP_PATH_TEMPLATE = - PathTemplate.createWithoutUrlEncoding("projects/{project}/groups/{group}"); - - /** Formats a string containing the fully-qualified path to represent a group resource. */ - public static final String formatGroupName(String project, String group) { - return GROUP_PATH_TEMPLATE.instantiate( - "project", project, - "group", group); - } - - /** Parses the project from the given fully-qualified path which represents a group resource. */ - public static final String parseProjectFromGroupName(String groupName) { - return GROUP_PATH_TEMPLATE.parse(groupName).get("project"); - } - - /** Parses the group from the given fully-qualified path which represents a group resource. */ - public static final String parseGroupFromGroupName(String groupName) { - return GROUP_PATH_TEMPLATE.parse(groupName).get("group"); - } - /** Constructs an instance of ErrorGroupServiceClient with default settings. */ public static final ErrorGroupServiceClient create() throws IOException { return create(ErrorGroupServiceSettings.defaultBuilder().build()); @@ -179,8 +159,8 @@ public final ErrorGroupServiceSettings getSettings() { * *

    * try (ErrorGroupServiceClient errorGroupServiceClient = ErrorGroupServiceClient.create()) {
-   *   String formattedGroupName = ErrorGroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
-   *   ErrorGroup response = errorGroupServiceClient.getGroup(formattedGroupName);
+   *   GroupName groupName = GroupName.create("[PROJECT]", "[GROUP]");
+   *   ErrorGroup response = errorGroupServiceClient.getGroup(groupName);
    * }
    * 
* @@ -192,9 +172,10 @@ public final ErrorGroupServiceSettings getSettings() { *

Example: <code>projects/my-project-123/groups/my-group</code> * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ - public final ErrorGroup getGroup(String groupName) { - GROUP_PATH_TEMPLATE.validate(groupName, "getGroup"); - GetGroupRequest request = GetGroupRequest.newBuilder().setGroupName(groupName).build(); + public final ErrorGroup getGroup(GroupName groupName) { + + GetGroupRequest request = + GetGroupRequest.newBuilder().setGroupNameWithGroupName(groupName).build(); return getGroup(request); } @@ -206,9 +187,9 @@ public final ErrorGroup getGroup(String groupName) { * *


    * try (ErrorGroupServiceClient errorGroupServiceClient = ErrorGroupServiceClient.create()) {
-   *   String formattedGroupName = ErrorGroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
+   *   GroupName groupName = GroupName.create("[PROJECT]", "[GROUP]");
    *   GetGroupRequest request = GetGroupRequest.newBuilder()
-   *     .setGroupName(formattedGroupName)
+   *     .setGroupNameWithGroupName(groupName)
    *     .build();
    *   ErrorGroup response = errorGroupServiceClient.getGroup(request);
    * }
@@ -229,9 +210,9 @@ private final ErrorGroup getGroup(GetGroupRequest request) {
    *
    * 

    * try (ErrorGroupServiceClient errorGroupServiceClient = ErrorGroupServiceClient.create()) {
-   *   String formattedGroupName = ErrorGroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
+   *   GroupName groupName = GroupName.create("[PROJECT]", "[GROUP]");
    *   GetGroupRequest request = GetGroupRequest.newBuilder()
-   *     .setGroupName(formattedGroupName)
+   *     .setGroupNameWithGroupName(groupName)
    *     .build();
    *   ListenableFuture<ErrorGroup> future = errorGroupServiceClient.getGroupCallable().futureCall(request);
    *   // Do something
diff --git a/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorStatsServiceClient.java b/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorStatsServiceClient.java
index a612ae883eb7..4a53ccaf92bb 100644
--- a/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorStatsServiceClient.java
+++ b/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorStatsServiceClient.java
@@ -20,13 +20,13 @@
 
 import com.google.api.gax.grpc.ChannelAndExecutor;
 import com.google.api.gax.grpc.UnaryCallable;
-import com.google.api.gax.protobuf.PathTemplate;
 import com.google.devtools.clouderrorreporting.v1beta1.DeleteEventsRequest;
 import com.google.devtools.clouderrorreporting.v1beta1.DeleteEventsResponse;
 import com.google.devtools.clouderrorreporting.v1beta1.ListEventsRequest;
 import com.google.devtools.clouderrorreporting.v1beta1.ListEventsResponse;
 import com.google.devtools.clouderrorreporting.v1beta1.ListGroupStatsRequest;
 import com.google.devtools.clouderrorreporting.v1beta1.ListGroupStatsResponse;
+import com.google.devtools.clouderrorreporting.v1beta1.ProjectName;
 import com.google.devtools.clouderrorreporting.v1beta1.QueryTimeRange;
 import com.google.protobuf.ExperimentalApi;
 import io.grpc.ManagedChannel;
@@ -48,8 +48,8 @@
  * 
  * 
  * try (ErrorStatsServiceClient errorStatsServiceClient = ErrorStatsServiceClient.create()) {
- *   String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]");
- *   DeleteEventsResponse response = errorStatsServiceClient.deleteEvents(formattedProjectName);
+ *   ProjectName projectName = ProjectName.create("[PROJECT]");
+ *   DeleteEventsResponse response = errorStatsServiceClient.deleteEvents(projectName);
  * }
  * 
  * 
@@ -109,19 +109,6 @@ public class ErrorStatsServiceClient implements AutoCloseable { private final UnaryCallable listEventsPagedCallable; private final UnaryCallable deleteEventsCallable; - private static final PathTemplate PROJECT_PATH_TEMPLATE = - PathTemplate.createWithoutUrlEncoding("projects/{project}"); - - /** Formats a string containing the fully-qualified path to represent a project resource. */ - public static final String formatProjectName(String project) { - return PROJECT_PATH_TEMPLATE.instantiate("project", project); - } - - /** Parses the project from the given fully-qualified path which represents a project resource. */ - public static final String parseProjectFromProjectName(String projectName) { - return PROJECT_PATH_TEMPLATE.parse(projectName).get("project"); - } - /** Constructs an instance of ErrorStatsServiceClient with default settings. */ public static final ErrorStatsServiceClient create() throws IOException { return create(ErrorStatsServiceSettings.defaultBuilder().build()); @@ -192,9 +179,9 @@ public final ErrorStatsServiceSettings getSettings() { * *

    * try (ErrorStatsServiceClient errorStatsServiceClient = ErrorStatsServiceClient.create()) {
-   *   String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
    *   QueryTimeRange timeRange = QueryTimeRange.newBuilder().build();
-   *   for (ErrorGroupStats element : errorStatsServiceClient.listGroupStats(formattedProjectName, timeRange).iterateAllElements()) {
+   *   for (ErrorGroupStats element : errorStatsServiceClient.listGroupStats(projectName, timeRange).iterateAllElements()) {
    *     // doThingsWith(element);
    *   }
    * }
@@ -205,18 +192,19 @@ public final ErrorStatsServiceSettings getSettings() {
    *     href="https://support.google.com/cloud/answer/6158840">Google Cloud Platform project
    *     ID</a>.
    *     

Example: <code>projects/my-project-123</code>. - * @param timeRange [Required] List data for the given time range. Only - * <code>ErrorGroupStats</code> with a non-zero count in the given time range are - * returned, unless the request contains an explicit group_id list. If a group_id list is - * given, also <code>ErrorGroupStats</code> with zero occurrences are returned. + * @param timeRange [Optional] List data for the given time range. If not set a default time range + * is used. The field time_range_begin in the response will specify the beginning of this time + * range. Only <code>ErrorGroupStats</code> with a non-zero count in the given + * time range are returned, unless the request contains an explicit group_id list. If a + * group_id list is given, also <code>ErrorGroupStats</code> with zero occurrences + * are returned. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final ListGroupStatsPagedResponse listGroupStats( - String projectName, QueryTimeRange timeRange) { - PROJECT_PATH_TEMPLATE.validate(projectName, "listGroupStats"); + ProjectName projectName, QueryTimeRange timeRange) { ListGroupStatsRequest request = ListGroupStatsRequest.newBuilder() - .setProjectName(projectName) + .setProjectNameWithProjectName(projectName) .setTimeRange(timeRange) .build(); return listGroupStats(request); @@ -230,10 +218,10 @@ public final ListGroupStatsPagedResponse listGroupStats( * *


    * try (ErrorStatsServiceClient errorStatsServiceClient = ErrorStatsServiceClient.create()) {
-   *   String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
    *   QueryTimeRange timeRange = QueryTimeRange.newBuilder().build();
    *   ListGroupStatsRequest request = ListGroupStatsRequest.newBuilder()
-   *     .setProjectName(formattedProjectName)
+   *     .setProjectNameWithProjectName(projectName)
    *     .setTimeRange(timeRange)
    *     .build();
    *   for (ErrorGroupStats element : errorStatsServiceClient.listGroupStats(request).iterateAllElements()) {
@@ -257,10 +245,10 @@ public final ListGroupStatsPagedResponse listGroupStats(ListGroupStatsRequest re
    *
    * 

    * try (ErrorStatsServiceClient errorStatsServiceClient = ErrorStatsServiceClient.create()) {
-   *   String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
    *   QueryTimeRange timeRange = QueryTimeRange.newBuilder().build();
    *   ListGroupStatsRequest request = ListGroupStatsRequest.newBuilder()
-   *     .setProjectName(formattedProjectName)
+   *     .setProjectNameWithProjectName(projectName)
    *     .setTimeRange(timeRange)
    *     .build();
    *   ListenableFuture<ListGroupStatsPagedResponse> future = errorStatsServiceClient.listGroupStatsPagedCallable().futureCall(request);
@@ -284,10 +272,10 @@ public final ListGroupStatsPagedResponse listGroupStats(ListGroupStatsRequest re
    *
    * 

    * try (ErrorStatsServiceClient errorStatsServiceClient = ErrorStatsServiceClient.create()) {
-   *   String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
    *   QueryTimeRange timeRange = QueryTimeRange.newBuilder().build();
    *   ListGroupStatsRequest request = ListGroupStatsRequest.newBuilder()
-   *     .setProjectName(formattedProjectName)
+   *     .setProjectNameWithProjectName(projectName)
    *     .setTimeRange(timeRange)
    *     .build();
    *   while (true) {
@@ -318,9 +306,9 @@ public final ListGroupStatsPagedResponse listGroupStats(ListGroupStatsRequest re
    *
    * 

    * try (ErrorStatsServiceClient errorStatsServiceClient = ErrorStatsServiceClient.create()) {
-   *   String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
    *   String groupId = "";
-   *   for (ErrorEvent element : errorStatsServiceClient.listEvents(formattedProjectName, groupId).iterateAllElements()) {
+   *   for (ErrorEvent element : errorStatsServiceClient.listEvents(projectName, groupId).iterateAllElements()) {
    *     // doThingsWith(element);
    *   }
    * }
@@ -332,10 +320,12 @@ public final ListGroupStatsPagedResponse listGroupStats(ListGroupStatsRequest re
    * @param groupId [Required] The group for which events shall be returned.
    * @throws com.google.api.gax.grpc.ApiException if the remote call fails
    */
-  public final ListEventsPagedResponse listEvents(String projectName, String groupId) {
-    PROJECT_PATH_TEMPLATE.validate(projectName, "listEvents");
+  public final ListEventsPagedResponse listEvents(ProjectName projectName, String groupId) {
     ListEventsRequest request =
-        ListEventsRequest.newBuilder().setProjectName(projectName).setGroupId(groupId).build();
+        ListEventsRequest.newBuilder()
+            .setProjectNameWithProjectName(projectName)
+            .setGroupId(groupId)
+            .build();
     return listEvents(request);
   }
 
@@ -347,10 +337,10 @@ public final ListEventsPagedResponse listEvents(String projectName, String group
    *
    * 

    * try (ErrorStatsServiceClient errorStatsServiceClient = ErrorStatsServiceClient.create()) {
-   *   String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
    *   String groupId = "";
    *   ListEventsRequest request = ListEventsRequest.newBuilder()
-   *     .setProjectName(formattedProjectName)
+   *     .setProjectNameWithProjectName(projectName)
    *     .setGroupId(groupId)
    *     .build();
    *   for (ErrorEvent element : errorStatsServiceClient.listEvents(request).iterateAllElements()) {
@@ -374,10 +364,10 @@ public final ListEventsPagedResponse listEvents(ListEventsRequest request) {
    *
    * 

    * try (ErrorStatsServiceClient errorStatsServiceClient = ErrorStatsServiceClient.create()) {
-   *   String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
    *   String groupId = "";
    *   ListEventsRequest request = ListEventsRequest.newBuilder()
-   *     .setProjectName(formattedProjectName)
+   *     .setProjectNameWithProjectName(projectName)
    *     .setGroupId(groupId)
    *     .build();
    *   ListenableFuture<ListEventsPagedResponse> future = errorStatsServiceClient.listEventsPagedCallable().futureCall(request);
@@ -400,10 +390,10 @@ public final UnaryCallable listEvent
    *
    * 

    * try (ErrorStatsServiceClient errorStatsServiceClient = ErrorStatsServiceClient.create()) {
-   *   String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
    *   String groupId = "";
    *   ListEventsRequest request = ListEventsRequest.newBuilder()
-   *     .setProjectName(formattedProjectName)
+   *     .setProjectNameWithProjectName(projectName)
    *     .setGroupId(groupId)
    *     .build();
    *   while (true) {
@@ -433,8 +423,8 @@ public final UnaryCallable listEventsCall
    *
    * 

    * try (ErrorStatsServiceClient errorStatsServiceClient = ErrorStatsServiceClient.create()) {
-   *   String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]");
-   *   DeleteEventsResponse response = errorStatsServiceClient.deleteEvents(formattedProjectName);
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
+   *   DeleteEventsResponse response = errorStatsServiceClient.deleteEvents(projectName);
    * }
    * 
* @@ -443,10 +433,10 @@ public final UnaryCallable listEventsCall * ID](https://support.google.com/cloud/answer/6158840). Example: `projects/my-project-123`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ - public final DeleteEventsResponse deleteEvents(String projectName) { - PROJECT_PATH_TEMPLATE.validate(projectName, "deleteEvents"); + public final DeleteEventsResponse deleteEvents(ProjectName projectName) { + DeleteEventsRequest request = - DeleteEventsRequest.newBuilder().setProjectName(projectName).build(); + DeleteEventsRequest.newBuilder().setProjectNameWithProjectName(projectName).build(); return deleteEvents(request); } @@ -458,9 +448,9 @@ public final DeleteEventsResponse deleteEvents(String projectName) { * *

    * try (ErrorStatsServiceClient errorStatsServiceClient = ErrorStatsServiceClient.create()) {
-   *   String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
    *   DeleteEventsRequest request = DeleteEventsRequest.newBuilder()
-   *     .setProjectName(formattedProjectName)
+   *     .setProjectNameWithProjectName(projectName)
    *     .build();
    *   DeleteEventsResponse response = errorStatsServiceClient.deleteEvents(request);
    * }
@@ -481,9 +471,9 @@ private final DeleteEventsResponse deleteEvents(DeleteEventsRequest request) {
    *
    * 

    * try (ErrorStatsServiceClient errorStatsServiceClient = ErrorStatsServiceClient.create()) {
-   *   String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
    *   DeleteEventsRequest request = DeleteEventsRequest.newBuilder()
-   *     .setProjectName(formattedProjectName)
+   *     .setProjectNameWithProjectName(projectName)
    *     .build();
    *   ListenableFuture<DeleteEventsResponse> future = errorStatsServiceClient.deleteEventsCallable().futureCall(request);
    *   // Do something
diff --git a/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/ReportErrorsServiceClient.java b/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/ReportErrorsServiceClient.java
index 65a6ef234b4e..8189c716e86c 100644
--- a/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/ReportErrorsServiceClient.java
+++ b/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/ReportErrorsServiceClient.java
@@ -17,7 +17,7 @@
 
 import com.google.api.gax.grpc.ChannelAndExecutor;
 import com.google.api.gax.grpc.UnaryCallable;
-import com.google.api.gax.protobuf.PathTemplate;
+import com.google.devtools.clouderrorreporting.v1beta1.ProjectName;
 import com.google.devtools.clouderrorreporting.v1beta1.ReportErrorEventRequest;
 import com.google.devtools.clouderrorreporting.v1beta1.ReportErrorEventResponse;
 import com.google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent;
@@ -40,9 +40,9 @@
  * 
  * 
  * try (ReportErrorsServiceClient reportErrorsServiceClient = ReportErrorsServiceClient.create()) {
- *   String formattedProjectName = ReportErrorsServiceClient.formatProjectName("[PROJECT]");
+ *   ProjectName projectName = ProjectName.create("[PROJECT]");
  *   ReportedErrorEvent event = ReportedErrorEvent.newBuilder().build();
- *   ReportErrorEventResponse response = reportErrorsServiceClient.reportErrorEvent(formattedProjectName, event);
+ *   ReportErrorEventResponse response = reportErrorsServiceClient.reportErrorEvent(projectName, event);
  * }
  * 
  * 
@@ -98,19 +98,6 @@ public class ReportErrorsServiceClient implements AutoCloseable { private final UnaryCallable reportErrorEventCallable; - private static final PathTemplate PROJECT_PATH_TEMPLATE = - PathTemplate.createWithoutUrlEncoding("projects/{project}"); - - /** Formats a string containing the fully-qualified path to represent a project resource. */ - public static final String formatProjectName(String project) { - return PROJECT_PATH_TEMPLATE.instantiate("project", project); - } - - /** Parses the project from the given fully-qualified path which represents a project resource. */ - public static final String parseProjectFromProjectName(String projectName) { - return PROJECT_PATH_TEMPLATE.parse(projectName).get("project"); - } - /** Constructs an instance of ReportErrorsServiceClient with default settings. */ public static final ReportErrorsServiceClient create() throws IOException { return create(ReportErrorsServiceSettings.defaultBuilder().build()); @@ -178,9 +165,9 @@ public final ReportErrorsServiceSettings getSettings() { * *

    * try (ReportErrorsServiceClient reportErrorsServiceClient = ReportErrorsServiceClient.create()) {
-   *   String formattedProjectName = ReportErrorsServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
    *   ReportedErrorEvent event = ReportedErrorEvent.newBuilder().build();
-   *   ReportErrorEventResponse response = reportErrorsServiceClient.reportErrorEvent(formattedProjectName, event);
+   *   ReportErrorEventResponse response = reportErrorsServiceClient.reportErrorEvent(projectName, event);
    * }
    * 
* @@ -191,10 +178,13 @@ public final ReportErrorsServiceSettings getSettings() { * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final ReportErrorEventResponse reportErrorEvent( - String projectName, ReportedErrorEvent event) { - PROJECT_PATH_TEMPLATE.validate(projectName, "reportErrorEvent"); + ProjectName projectName, ReportedErrorEvent event) { + ReportErrorEventRequest request = - ReportErrorEventRequest.newBuilder().setProjectName(projectName).setEvent(event).build(); + ReportErrorEventRequest.newBuilder() + .setProjectNameWithProjectName(projectName) + .setEvent(event) + .build(); return reportErrorEvent(request); } @@ -213,10 +203,10 @@ public final ReportErrorEventResponse reportErrorEvent( * *

    * try (ReportErrorsServiceClient reportErrorsServiceClient = ReportErrorsServiceClient.create()) {
-   *   String formattedProjectName = ReportErrorsServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
    *   ReportedErrorEvent event = ReportedErrorEvent.newBuilder().build();
    *   ReportErrorEventRequest request = ReportErrorEventRequest.newBuilder()
-   *     .setProjectName(formattedProjectName)
+   *     .setProjectNameWithProjectName(projectName)
    *     .setEvent(event)
    *     .build();
    *   ReportErrorEventResponse response = reportErrorsServiceClient.reportErrorEvent(request);
@@ -245,10 +235,10 @@ public final ReportErrorEventResponse reportErrorEvent(ReportErrorEventRequest r
    *
    * 

    * try (ReportErrorsServiceClient reportErrorsServiceClient = ReportErrorsServiceClient.create()) {
-   *   String formattedProjectName = ReportErrorsServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName projectName = ProjectName.create("[PROJECT]");
    *   ReportedErrorEvent event = ReportedErrorEvent.newBuilder().build();
    *   ReportErrorEventRequest request = ReportErrorEventRequest.newBuilder()
-   *     .setProjectName(formattedProjectName)
+   *     .setProjectNameWithProjectName(projectName)
    *     .setEvent(event)
    *     .build();
    *   ListenableFuture<ReportErrorEventResponse> future = reportErrorsServiceClient.reportErrorEventCallable().futureCall(request);
diff --git a/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/package-info.java b/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/package-info.java
index 5254c3f2104a..f48abbe98e8d 100644
--- a/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/package-info.java
+++ b/google-cloud-errorreporting/src/main/java/com/google/cloud/errorreporting/spi/v1beta1/package-info.java
@@ -28,8 +28,8 @@
  * 
  * 
  * try (ErrorGroupServiceClient errorGroupServiceClient = ErrorGroupServiceClient.create()) {
- *   String formattedGroupName = ErrorGroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
- *   ErrorGroup response = errorGroupServiceClient.getGroup(formattedGroupName);
+ *   GroupName groupName = GroupName.create("[PROJECT]", "[GROUP]");
+ *   ErrorGroup response = errorGroupServiceClient.getGroup(groupName);
  * }
  * 
  * 
@@ -44,8 +44,8 @@ *
  * 
  * try (ErrorStatsServiceClient errorStatsServiceClient = ErrorStatsServiceClient.create()) {
- *   String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]");
- *   DeleteEventsResponse response = errorStatsServiceClient.deleteEvents(formattedProjectName);
+ *   ProjectName projectName = ProjectName.create("[PROJECT]");
+ *   DeleteEventsResponse response = errorStatsServiceClient.deleteEvents(projectName);
  * }
  * 
  * 
@@ -59,9 +59,9 @@ *
  * 
  * try (ReportErrorsServiceClient reportErrorsServiceClient = ReportErrorsServiceClient.create()) {
- *   String formattedProjectName = ReportErrorsServiceClient.formatProjectName("[PROJECT]");
+ *   ProjectName projectName = ProjectName.create("[PROJECT]");
  *   ReportedErrorEvent event = ReportedErrorEvent.newBuilder().build();
- *   ReportErrorEventResponse response = reportErrorsServiceClient.reportErrorEvent(formattedProjectName, event);
+ *   ReportErrorEventResponse response = reportErrorsServiceClient.reportErrorEvent(projectName, event);
  * }
  * 
  * 
diff --git a/google-cloud-errorreporting/src/test/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorGroupServiceTest.java b/google-cloud-errorreporting/src/test/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorGroupServiceTest.java index f58d6313e864..b11c5cb6fd72 100644 --- a/google-cloud-errorreporting/src/test/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorGroupServiceTest.java +++ b/google-cloud-errorreporting/src/test/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorGroupServiceTest.java @@ -20,6 +20,7 @@ import com.google.api.gax.testing.MockServiceHelper; import com.google.devtools.clouderrorreporting.v1beta1.ErrorGroup; import com.google.devtools.clouderrorreporting.v1beta1.GetGroupRequest; +import com.google.devtools.clouderrorreporting.v1beta1.GroupName; import com.google.devtools.clouderrorreporting.v1beta1.UpdateGroupRequest; import com.google.protobuf.GeneratedMessageV3; import io.grpc.Status; @@ -78,21 +79,22 @@ public void tearDown() throws Exception { @Test @SuppressWarnings("all") public void getGroupTest() { - String name = "name3373707"; + GroupName name = GroupName.create("[PROJECT]", "[GROUP]"); String groupId = "groupId506361563"; - ErrorGroup expectedResponse = ErrorGroup.newBuilder().setName(name).setGroupId(groupId).build(); + ErrorGroup expectedResponse = + ErrorGroup.newBuilder().setNameWithGroupName(name).setGroupId(groupId).build(); mockErrorGroupService.addResponse(expectedResponse); - String formattedGroupName = ErrorGroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]"); + GroupName groupName = GroupName.create("[PROJECT]", "[GROUP]"); - ErrorGroup actualResponse = client.getGroup(formattedGroupName); + ErrorGroup actualResponse = client.getGroup(groupName); Assert.assertEquals(expectedResponse, actualResponse); List actualRequests = mockErrorGroupService.getRequests(); Assert.assertEquals(1, actualRequests.size()); GetGroupRequest actualRequest = (GetGroupRequest) actualRequests.get(0); - Assert.assertEquals(formattedGroupName, actualRequest.getGroupName()); + Assert.assertEquals(groupName, actualRequest.getGroupNameAsGroupName()); } @Test @@ -102,9 +104,9 @@ public void getGroupExceptionTest() throws Exception { mockErrorGroupService.addException(exception); try { - String formattedGroupName = ErrorGroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]"); + GroupName groupName = GroupName.create("[PROJECT]", "[GROUP]"); - client.getGroup(formattedGroupName); + client.getGroup(groupName); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); @@ -114,9 +116,10 @@ public void getGroupExceptionTest() throws Exception { @Test @SuppressWarnings("all") public void updateGroupTest() { - String name = "name3373707"; + GroupName name = GroupName.create("[PROJECT]", "[GROUP]"); String groupId = "groupId506361563"; - ErrorGroup expectedResponse = ErrorGroup.newBuilder().setName(name).setGroupId(groupId).build(); + ErrorGroup expectedResponse = + ErrorGroup.newBuilder().setNameWithGroupName(name).setGroupId(groupId).build(); mockErrorGroupService.addResponse(expectedResponse); ErrorGroup group = ErrorGroup.newBuilder().build(); diff --git a/google-cloud-errorreporting/src/test/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorStatsServiceTest.java b/google-cloud-errorreporting/src/test/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorStatsServiceTest.java index 324a3caaf617..9d8250cbc24a 100644 --- a/google-cloud-errorreporting/src/test/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorStatsServiceTest.java +++ b/google-cloud-errorreporting/src/test/java/com/google/cloud/errorreporting/spi/v1beta1/ErrorStatsServiceTest.java @@ -30,6 +30,7 @@ import com.google.devtools.clouderrorreporting.v1beta1.ListEventsResponse; import com.google.devtools.clouderrorreporting.v1beta1.ListGroupStatsRequest; import com.google.devtools.clouderrorreporting.v1beta1.ListGroupStatsResponse; +import com.google.devtools.clouderrorreporting.v1beta1.ProjectName; import com.google.devtools.clouderrorreporting.v1beta1.QueryTimeRange; import com.google.protobuf.GeneratedMessageV3; import io.grpc.Status; @@ -98,11 +99,10 @@ public void listGroupStatsTest() { .build(); mockErrorStatsService.addResponse(expectedResponse); - String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]"); + ProjectName projectName = ProjectName.create("[PROJECT]"); QueryTimeRange timeRange = QueryTimeRange.newBuilder().build(); - ListGroupStatsPagedResponse pagedListResponse = - client.listGroupStats(formattedProjectName, timeRange); + ListGroupStatsPagedResponse pagedListResponse = client.listGroupStats(projectName, timeRange); List resources = Lists.newArrayList(pagedListResponse.iterateAllElements()); Assert.assertEquals(1, resources.size()); @@ -112,7 +112,7 @@ public void listGroupStatsTest() { Assert.assertEquals(1, actualRequests.size()); ListGroupStatsRequest actualRequest = (ListGroupStatsRequest) actualRequests.get(0); - Assert.assertEquals(formattedProjectName, actualRequest.getProjectName()); + Assert.assertEquals(projectName, actualRequest.getProjectNameAsProjectName()); Assert.assertEquals(timeRange, actualRequest.getTimeRange()); } @@ -123,10 +123,10 @@ public void listGroupStatsExceptionTest() throws Exception { mockErrorStatsService.addException(exception); try { - String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]"); + ProjectName projectName = ProjectName.create("[PROJECT]"); QueryTimeRange timeRange = QueryTimeRange.newBuilder().build(); - client.listGroupStats(formattedProjectName, timeRange); + client.listGroupStats(projectName, timeRange); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); @@ -146,10 +146,10 @@ public void listEventsTest() { .build(); mockErrorStatsService.addResponse(expectedResponse); - String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]"); + ProjectName projectName = ProjectName.create("[PROJECT]"); String groupId = "groupId506361563"; - ListEventsPagedResponse pagedListResponse = client.listEvents(formattedProjectName, groupId); + ListEventsPagedResponse pagedListResponse = client.listEvents(projectName, groupId); List resources = Lists.newArrayList(pagedListResponse.iterateAllElements()); Assert.assertEquals(1, resources.size()); @@ -159,7 +159,7 @@ public void listEventsTest() { Assert.assertEquals(1, actualRequests.size()); ListEventsRequest actualRequest = (ListEventsRequest) actualRequests.get(0); - Assert.assertEquals(formattedProjectName, actualRequest.getProjectName()); + Assert.assertEquals(projectName, actualRequest.getProjectNameAsProjectName()); Assert.assertEquals(groupId, actualRequest.getGroupId()); } @@ -170,10 +170,10 @@ public void listEventsExceptionTest() throws Exception { mockErrorStatsService.addException(exception); try { - String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]"); + ProjectName projectName = ProjectName.create("[PROJECT]"); String groupId = "groupId506361563"; - client.listEvents(formattedProjectName, groupId); + client.listEvents(projectName, groupId); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); @@ -186,16 +186,16 @@ public void deleteEventsTest() { DeleteEventsResponse expectedResponse = DeleteEventsResponse.newBuilder().build(); mockErrorStatsService.addResponse(expectedResponse); - String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]"); + ProjectName projectName = ProjectName.create("[PROJECT]"); - DeleteEventsResponse actualResponse = client.deleteEvents(formattedProjectName); + DeleteEventsResponse actualResponse = client.deleteEvents(projectName); Assert.assertEquals(expectedResponse, actualResponse); List actualRequests = mockErrorStatsService.getRequests(); Assert.assertEquals(1, actualRequests.size()); DeleteEventsRequest actualRequest = (DeleteEventsRequest) actualRequests.get(0); - Assert.assertEquals(formattedProjectName, actualRequest.getProjectName()); + Assert.assertEquals(projectName, actualRequest.getProjectNameAsProjectName()); } @Test @@ -205,9 +205,9 @@ public void deleteEventsExceptionTest() throws Exception { mockErrorStatsService.addException(exception); try { - String formattedProjectName = ErrorStatsServiceClient.formatProjectName("[PROJECT]"); + ProjectName projectName = ProjectName.create("[PROJECT]"); - client.deleteEvents(formattedProjectName); + client.deleteEvents(projectName); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); diff --git a/google-cloud-errorreporting/src/test/java/com/google/cloud/errorreporting/spi/v1beta1/ReportErrorsServiceTest.java b/google-cloud-errorreporting/src/test/java/com/google/cloud/errorreporting/spi/v1beta1/ReportErrorsServiceTest.java index 70b1bf877670..913840ab9fe1 100644 --- a/google-cloud-errorreporting/src/test/java/com/google/cloud/errorreporting/spi/v1beta1/ReportErrorsServiceTest.java +++ b/google-cloud-errorreporting/src/test/java/com/google/cloud/errorreporting/spi/v1beta1/ReportErrorsServiceTest.java @@ -18,6 +18,7 @@ import com.google.api.gax.grpc.ApiException; import com.google.api.gax.testing.MockGrpcService; import com.google.api.gax.testing.MockServiceHelper; +import com.google.devtools.clouderrorreporting.v1beta1.ProjectName; import com.google.devtools.clouderrorreporting.v1beta1.ReportErrorEventRequest; import com.google.devtools.clouderrorreporting.v1beta1.ReportErrorEventResponse; import com.google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent; @@ -81,17 +82,17 @@ public void reportErrorEventTest() { ReportErrorEventResponse expectedResponse = ReportErrorEventResponse.newBuilder().build(); mockReportErrorsService.addResponse(expectedResponse); - String formattedProjectName = ReportErrorsServiceClient.formatProjectName("[PROJECT]"); + ProjectName projectName = ProjectName.create("[PROJECT]"); ReportedErrorEvent event = ReportedErrorEvent.newBuilder().build(); - ReportErrorEventResponse actualResponse = client.reportErrorEvent(formattedProjectName, event); + ReportErrorEventResponse actualResponse = client.reportErrorEvent(projectName, event); Assert.assertEquals(expectedResponse, actualResponse); List actualRequests = mockReportErrorsService.getRequests(); Assert.assertEquals(1, actualRequests.size()); ReportErrorEventRequest actualRequest = (ReportErrorEventRequest) actualRequests.get(0); - Assert.assertEquals(formattedProjectName, actualRequest.getProjectName()); + Assert.assertEquals(projectName, actualRequest.getProjectNameAsProjectName()); Assert.assertEquals(event, actualRequest.getEvent()); } @@ -102,10 +103,10 @@ public void reportErrorEventExceptionTest() throws Exception { mockReportErrorsService.addException(exception); try { - String formattedProjectName = ReportErrorsServiceClient.formatProjectName("[PROJECT]"); + ProjectName projectName = ProjectName.create("[PROJECT]"); ReportedErrorEvent event = ReportedErrorEvent.newBuilder().build(); - client.reportErrorEvent(formattedProjectName, event); + client.reportErrorEvent(projectName, event); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); diff --git a/google-cloud-language/pom.xml b/google-cloud-language/pom.xml index 2d8c6ed025ad..db3b8fb82678 100644 --- a/google-cloud-language/pom.xml +++ b/google-cloud-language/pom.xml @@ -30,7 +30,7 @@ com.google.api.grpc grpc-google-cloud-language-v1 - 0.1.3 + 0.1.5 io.grpc diff --git a/google-cloud-logging/pom.xml b/google-cloud-logging/pom.xml index 9c5e73155481..5ea3038055b1 100644 --- a/google-cloud-logging/pom.xml +++ b/google-cloud-logging/pom.xml @@ -31,7 +31,7 @@ com.google.api.grpc grpc-google-cloud-logging-v2 - 0.1.4 + 0.1.5 io.grpc diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/ConfigServiceV2Client.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/ConfigServiceV2Client.java index c6e570a2b577..6a930936adf8 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/ConfigServiceV2Client.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/ConfigServiceV2Client.java @@ -184,8 +184,8 @@ public final ConfigServiceV2Settings getSettings() { * } *
* - * @param parent Required. The resource name where this sink was created: - *

"projects/[PROJECT_ID]" "organizations/[ORGANIZATION_ID]" + * @param parent Required. The parent resource whose sinks are to be listed. Examples: + * `"projects/my-logging-project"`, `"organizations/123456789"`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final ListSinksPagedResponse listSinks(ParentNameOneof parent) { @@ -287,9 +287,10 @@ public final UnaryCallable listSinksCallabl * } *

* - * @param sinkName Required. The resource name of the sink to return: + * @param sinkName Required. The parent resource name of the sink: *

"projects/[PROJECT_ID]/sinks/[SINK_ID]" * "organizations/[ORGANIZATION_ID]/sinks/[SINK_ID]" + *

Example: `"projects/my-project-id/sinks/my-sink-id"`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final LogSink getSink(SinkNameOneof sinkName) { @@ -346,7 +347,10 @@ public final UnaryCallable getSinkCallable() { // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Creates a sink. + * Creates a sink that exports specified log entries to a destination. The export of + * newly-ingested log entries begins immediately, unless the current time is outside the sink's + * start and end times or the sink's `writer_identity` is not permitted to write to the + * destination. A sink can export log entries only from the resource owning the sink. * *

Sample code: * @@ -360,6 +364,7 @@ public final UnaryCallable getSinkCallable() { * * @param parent Required. The resource in which to create the sink: *

"projects/[PROJECT_ID]" "organizations/[ORGANIZATION_ID]" + *

Examples: `"projects/my-logging-project"`, `"organizations/123456789"`. * @param sink Required. The new sink, whose `name` parameter is a sink identifier that is not * already in use. * @throws com.google.api.gax.grpc.ApiException if the remote call fails @@ -373,7 +378,10 @@ public final LogSink createSink(ParentNameOneof parent, LogSink sink) { // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Creates a sink. + * Creates a sink that exports specified log entries to a destination. The export of + * newly-ingested log entries begins immediately, unless the current time is outside the sink's + * start and end times or the sink's `writer_identity` is not permitted to write to the + * destination. A sink can export log entries only from the resource owning the sink. * *

Sample code: * @@ -398,7 +406,10 @@ public final LogSink createSink(CreateSinkRequest request) { // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Creates a sink. + * Creates a sink that exports specified log entries to a destination. The export of + * newly-ingested log entries begins immediately, unless the current time is outside the sink's + * start and end times or the sink's `writer_identity` is not permitted to write to the + * destination. A sink can export log entries only from the resource owning the sink. * *

Sample code: * @@ -422,7 +433,12 @@ public final UnaryCallable createSinkCallable() { // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Updates or creates a sink. + * Updates a sink. If the named sink doesn't exist, then this method is identical to + * [sinks.create](/logging/docs/api/reference/rest/v2/projects.sinks/create). If the named sink + * does exist, then this method replaces the following fields in the existing sink with values + * from the new sink: `destination`, `filter`, `output_version_format`, `start_time`, and + * `end_time`. The updated filter might also have a new `writer_identity`; see the + * `unique_writer_identity` field. * *

Sample code: * @@ -434,13 +450,13 @@ public final UnaryCallable createSinkCallable() { * } *

* - * @param sinkName Required. The resource name of the sink to update, including the parent + * @param sinkName Required. The full resource name of the sink to update, including the parent * resource and the sink identifier: *

"projects/[PROJECT_ID]/sinks/[SINK_ID]" * "organizations/[ORGANIZATION_ID]/sinks/[SINK_ID]" *

Example: `"projects/my-project-id/sinks/my-sink-id"`. * @param sink Required. The updated sink, whose name is the same identifier that appears as part - * of `sinkName`. If `sinkName` does not exist, then this method creates a new sink. + * of `sink_name`. If `sink_name` does not exist, then this method creates a new sink. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final LogSink updateSink(SinkNameOneof sinkName, LogSink sink) { @@ -452,7 +468,12 @@ public final LogSink updateSink(SinkNameOneof sinkName, LogSink sink) { // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Updates or creates a sink. + * Updates a sink. If the named sink doesn't exist, then this method is identical to + * [sinks.create](/logging/docs/api/reference/rest/v2/projects.sinks/create). If the named sink + * does exist, then this method replaces the following fields in the existing sink with values + * from the new sink: `destination`, `filter`, `output_version_format`, `start_time`, and + * `end_time`. The updated filter might also have a new `writer_identity`; see the + * `unique_writer_identity` field. * *

Sample code: * @@ -477,7 +498,12 @@ public final LogSink updateSink(UpdateSinkRequest request) { // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Updates or creates a sink. + * Updates a sink. If the named sink doesn't exist, then this method is identical to + * [sinks.create](/logging/docs/api/reference/rest/v2/projects.sinks/create). If the named sink + * does exist, then this method replaces the following fields in the existing sink with values + * from the new sink: `destination`, `filter`, `output_version_format`, `start_time`, and + * `end_time`. The updated filter might also have a new `writer_identity`; see the + * `unique_writer_identity` field. * *

Sample code: * @@ -501,7 +527,8 @@ public final UnaryCallable updateSinkCallable() { // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Deletes a sink. + * Deletes a sink. If the sink has a unique `writer_identity`, then that service account is also + * deleted. * *

Sample code: * @@ -512,11 +539,12 @@ public final UnaryCallable updateSinkCallable() { * } *

* - * @param sinkName Required. The resource name of the sink to delete, including the parent + * @param sinkName Required. The full resource name of the sink to delete, including the parent * resource and the sink identifier: *

"projects/[PROJECT_ID]/sinks/[SINK_ID]" * "organizations/[ORGANIZATION_ID]/sinks/[SINK_ID]" - *

It is an error if the sink does not exist. + *

It is an error if the sink does not exist. Example: + * `"projects/my-project-id/sinks/my-sink-id"`. It is an error if the sink does not exist. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final void deleteSink(SinkNameOneof sinkName) { @@ -528,7 +556,8 @@ public final void deleteSink(SinkNameOneof sinkName) { // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Deletes a sink. + * Deletes a sink. If the sink has a unique `writer_identity`, then that service account is also + * deleted. * *

Sample code: * @@ -551,7 +580,8 @@ private final void deleteSink(DeleteSinkRequest request) { // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Deletes a sink. + * Deletes a sink. If the sink has a unique `writer_identity`, then that service account is also + * deleted. * *

Sample code: * diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Client.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Client.java index 6948bba84a87..fd83a4e2471e 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Client.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Client.java @@ -16,6 +16,7 @@ package com.google.cloud.logging.spi.v2; import static com.google.cloud.logging.spi.v2.PagedResponseWrappers.ListLogEntriesPagedResponse; +import static com.google.cloud.logging.spi.v2.PagedResponseWrappers.ListLogsPagedResponse; import static com.google.cloud.logging.spi.v2.PagedResponseWrappers.ListMonitoredResourceDescriptorsPagedResponse; import com.google.api.MonitoredResource; @@ -24,10 +25,13 @@ import com.google.logging.v2.DeleteLogRequest; import com.google.logging.v2.ListLogEntriesRequest; import com.google.logging.v2.ListLogEntriesResponse; +import com.google.logging.v2.ListLogsRequest; +import com.google.logging.v2.ListLogsResponse; import com.google.logging.v2.ListMonitoredResourceDescriptorsRequest; import com.google.logging.v2.ListMonitoredResourceDescriptorsResponse; import com.google.logging.v2.LogEntry; import com.google.logging.v2.LogNameOneof; +import com.google.logging.v2.ParentNameOneof; import com.google.logging.v2.WriteLogEntriesRequest; import com.google.logging.v2.WriteLogEntriesResponse; import com.google.protobuf.Empty; @@ -115,6 +119,8 @@ public class LoggingServiceV2Client implements AutoCloseable { private final UnaryCallable< ListMonitoredResourceDescriptorsRequest, ListMonitoredResourceDescriptorsPagedResponse> listMonitoredResourceDescriptorsPagedCallable; + private final UnaryCallable listLogsCallable; + private final UnaryCallable listLogsPagedCallable; /** Constructs an instance of LoggingServiceV2Client with default settings. */ public static final LoggingServiceV2Client create() throws IOException { @@ -156,6 +162,10 @@ protected LoggingServiceV2Client(LoggingServiceV2Settings settings) throws IOExc this.listMonitoredResourceDescriptorsPagedCallable = UnaryCallable.createPagedVariant( settings.listMonitoredResourceDescriptorsSettings(), this.channel, this.executor); + this.listLogsCallable = + UnaryCallable.create(settings.listLogsSettings(), this.channel, this.executor); + this.listLogsPagedCallable = + UnaryCallable.createPagedVariant(settings.listLogsSettings(), this.channel, this.executor); if (settings.getChannelProvider().shouldAutoClose()) { closeables.add( @@ -356,8 +366,8 @@ public final WriteLogEntriesResponse writeLogEntries(WriteLogEntriesRequest requ // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Lists log entries. Use this method to retrieve log entries from Cloud Logging. For ways to - * export log entries, see [Exporting Logs](/logging/docs/export). + * Lists log entries. Use this method to retrieve log entries from Stackdriver Logging. For ways + * to export log entries, see [Exporting Logs](/logging/docs/export). * *

Sample code: * @@ -372,13 +382,16 @@ public final WriteLogEntriesResponse writeLogEntries(WriteLogEntriesRequest requ * } *

* - * @param resourceNames Required. One or more cloud resources from which to retrieve log entries: + * @param resourceNames Required. Names of one or more resources from which to retrieve log + * entries: *

"projects/[PROJECT_ID]" "organizations/[ORGANIZATION_ID]" *

Projects listed in the `project_ids` field are added to this list. * @param filter Optional. A filter that chooses which log entries to return. See [Advanced Logs * Filters](/logging/docs/view/advanced_filters). Only log entries that match the filter are - * returned. An empty filter matches all log entries. The maximum length of the filter is - * 20000 characters. + * returned. An empty filter matches all log entries in the resources listed in + * `resource_names`. Referencing a parent resource that is not listed in `resource_names` will + * cause the filter to return no results. The maximum length of the filter is 20000 + * characters. * @param orderBy Optional. How the results should be sorted. Presently, the only permitted values * are `"timestamp asc"` (default) and `"timestamp desc"`. The first option returns entries in * order of increasing values of `LogEntry.timestamp` (oldest first), and the second option @@ -399,8 +412,8 @@ public final ListLogEntriesPagedResponse listLogEntries( // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Lists log entries. Use this method to retrieve log entries from Cloud Logging. For ways to - * export log entries, see [Exporting Logs](/logging/docs/export). + * Lists log entries. Use this method to retrieve log entries from Stackdriver Logging. For ways + * to export log entries, see [Exporting Logs](/logging/docs/export). * *

Sample code: * @@ -425,8 +438,8 @@ public final ListLogEntriesPagedResponse listLogEntries(ListLogEntriesRequest re // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Lists log entries. Use this method to retrieve log entries from Cloud Logging. For ways to - * export log entries, see [Exporting Logs](/logging/docs/export). + * Lists log entries. Use this method to retrieve log entries from Stackdriver Logging. For ways + * to export log entries, see [Exporting Logs](/logging/docs/export). * *

Sample code: * @@ -451,8 +464,8 @@ public final ListLogEntriesPagedResponse listLogEntries(ListLogEntriesRequest re // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Lists log entries. Use this method to retrieve log entries from Cloud Logging. For ways to - * export log entries, see [Exporting Logs](/logging/docs/export). + * Lists log entries. Use this method to retrieve log entries from Stackdriver Logging. For ways + * to export log entries, see [Exporting Logs](/logging/docs/export). * *

Sample code: * @@ -484,7 +497,7 @@ public final ListLogEntriesPagedResponse listLogEntries(ListLogEntriesRequest re // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Lists the monitored resource descriptors used by Stackdriver Logging. + * Lists the descriptors for monitored resource types used by Stackdriver Logging. * *

Sample code: * @@ -507,7 +520,7 @@ public final ListMonitoredResourceDescriptorsPagedResponse listMonitoredResource // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Lists the monitored resource descriptors used by Stackdriver Logging. + * Lists the descriptors for monitored resource types used by Stackdriver Logging. * *

Sample code: * @@ -530,7 +543,7 @@ public final ListMonitoredResourceDescriptorsPagedResponse listMonitoredResource // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Lists the monitored resource descriptors used by Stackdriver Logging. + * Lists the descriptors for monitored resource types used by Stackdriver Logging. * *

Sample code: * @@ -558,6 +571,111 @@ public final ListMonitoredResourceDescriptorsPagedResponse listMonitoredResource return listMonitoredResourceDescriptorsCallable; } + // AUTO-GENERATED DOCUMENTATION AND METHOD + /** + * Lists the logs in projects or organizations. Only logs that have entries are listed. + * + *

Sample code: + * + *


+   * try (LoggingServiceV2Client loggingServiceV2Client = LoggingServiceV2Client.create()) {
+   *   ParentNameOneof parent = ParentNameOneof.from(ProjectName.create("[PROJECT]"));
+   *   for (String element : loggingServiceV2Client.listLogs(parent).iterateAllElements()) {
+   *     // doThingsWith(element);
+   *   }
+   * }
+   * 
+ * + * @param parent Required. The resource name that owns the logs: + *

"projects/[PROJECT_ID]" "organizations/[ORGANIZATION_ID]" + * @throws com.google.api.gax.grpc.ApiException if the remote call fails + */ + public final ListLogsPagedResponse listLogs(ParentNameOneof parent) { + ListLogsRequest request = + ListLogsRequest.newBuilder().setParentWithParentNameOneof(parent).build(); + return listLogs(request); + } + + // AUTO-GENERATED DOCUMENTATION AND METHOD + /** + * Lists the logs in projects or organizations. Only logs that have entries are listed. + * + *

Sample code: + * + *


+   * try (LoggingServiceV2Client loggingServiceV2Client = LoggingServiceV2Client.create()) {
+   *   ParentNameOneof parent = ParentNameOneof.from(ProjectName.create("[PROJECT]"));
+   *   ListLogsRequest request = ListLogsRequest.newBuilder()
+   *     .setParentWithParentNameOneof(parent)
+   *     .build();
+   *   for (String element : loggingServiceV2Client.listLogs(request).iterateAllElements()) {
+   *     // doThingsWith(element);
+   *   }
+   * }
+   * 
+ * + * @param request The request object containing all of the parameters for the API call. + * @throws com.google.api.gax.grpc.ApiException if the remote call fails + */ + public final ListLogsPagedResponse listLogs(ListLogsRequest request) { + return listLogsPagedCallable().call(request); + } + + // AUTO-GENERATED DOCUMENTATION AND METHOD + /** + * Lists the logs in projects or organizations. Only logs that have entries are listed. + * + *

Sample code: + * + *


+   * try (LoggingServiceV2Client loggingServiceV2Client = LoggingServiceV2Client.create()) {
+   *   ParentNameOneof parent = ParentNameOneof.from(ProjectName.create("[PROJECT]"));
+   *   ListLogsRequest request = ListLogsRequest.newBuilder()
+   *     .setParentWithParentNameOneof(parent)
+   *     .build();
+   *   ListenableFuture<ListLogsPagedResponse> future = loggingServiceV2Client.listLogsPagedCallable().futureCall(request);
+   *   // Do something
+   *   for (String element : future.get().iterateAllElements()) {
+   *     // doThingsWith(element);
+   *   }
+   * }
+   * 
+ */ + public final UnaryCallable listLogsPagedCallable() { + return listLogsPagedCallable; + } + + // AUTO-GENERATED DOCUMENTATION AND METHOD + /** + * Lists the logs in projects or organizations. Only logs that have entries are listed. + * + *

Sample code: + * + *


+   * try (LoggingServiceV2Client loggingServiceV2Client = LoggingServiceV2Client.create()) {
+   *   ParentNameOneof parent = ParentNameOneof.from(ProjectName.create("[PROJECT]"));
+   *   ListLogsRequest request = ListLogsRequest.newBuilder()
+   *     .setParentWithParentNameOneof(parent)
+   *     .build();
+   *   while (true) {
+   *     ListLogsResponse response = loggingServiceV2Client.listLogsCallable().call(request);
+   *     for (String element : response.getLogNamesList()) {
+   *       // doThingsWith(element);
+   *     }
+   *     String nextPageToken = response.getNextPageToken();
+   *     if (!Strings.isNullOrEmpty(nextPageToken)) {
+   *       request = request.toBuilder().setPageToken(nextPageToken).build();
+   *     } else {
+   *       break;
+   *     }
+   *   }
+   * }
+   * 
+ */ + public final UnaryCallable listLogsCallable() { + return listLogsCallable; + } + /** * Initiates an orderly shutdown in which preexisting calls continue but new calls are immediately * cancelled. diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Settings.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Settings.java index 3d643d08dbb4..e854c3fcf872 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Settings.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Settings.java @@ -16,6 +16,7 @@ package com.google.cloud.logging.spi.v2; import static com.google.cloud.logging.spi.v2.PagedResponseWrappers.ListLogEntriesPagedResponse; +import static com.google.cloud.logging.spi.v2.PagedResponseWrappers.ListLogsPagedResponse; import static com.google.cloud.logging.spi.v2.PagedResponseWrappers.ListMonitoredResourceDescriptorsPagedResponse; import com.google.api.MonitoredResourceDescriptor; @@ -41,6 +42,8 @@ import com.google.logging.v2.DeleteLogRequest; import com.google.logging.v2.ListLogEntriesRequest; import com.google.logging.v2.ListLogEntriesResponse; +import com.google.logging.v2.ListLogsRequest; +import com.google.logging.v2.ListLogsResponse; import com.google.logging.v2.ListMonitoredResourceDescriptorsRequest; import com.google.logging.v2.ListMonitoredResourceDescriptorsResponse; import com.google.logging.v2.LogEntry; @@ -109,6 +112,8 @@ public class LoggingServiceV2Settings extends ClientSettings { ListMonitoredResourceDescriptorsRequest, ListMonitoredResourceDescriptorsResponse, ListMonitoredResourceDescriptorsPagedResponse> listMonitoredResourceDescriptorsSettings; + private final PagedCallSettings + listLogsSettings; /** Returns the object with the settings used for calls to deleteLog. */ public SimpleCallSettings deleteLogSettings() { @@ -136,6 +141,12 @@ public SimpleCallSettings deleteLogSettings() { return listMonitoredResourceDescriptorsSettings; } + /** Returns the object with the settings used for calls to listLogs. */ + public PagedCallSettings + listLogsSettings() { + return listLogsSettings; + } + /** Returns a builder for the default ExecutorProvider for this service. */ public static InstantiatingExecutorProvider.Builder defaultExecutorProviderBuilder() { return InstantiatingExecutorProvider.newBuilder(); @@ -192,6 +203,7 @@ private LoggingServiceV2Settings(Builder settingsBuilder) throws IOException { listLogEntriesSettings = settingsBuilder.listLogEntriesSettings().build(); listMonitoredResourceDescriptorsSettings = settingsBuilder.listMonitoredResourceDescriptorsSettings().build(); + listLogsSettings = settingsBuilder.listLogsSettings().build(); } private static final PagedListDescriptor @@ -274,6 +286,40 @@ public Iterable extractResources( } }; + private static final PagedListDescriptor + LIST_LOGS_PAGE_STR_DESC = + new PagedListDescriptor() { + @Override + public Object emptyToken() { + return ""; + } + + @Override + public ListLogsRequest injectToken(ListLogsRequest payload, Object token) { + return ListLogsRequest.newBuilder(payload).setPageToken((String) token).build(); + } + + @Override + public ListLogsRequest injectPageSize(ListLogsRequest payload, int pageSize) { + return ListLogsRequest.newBuilder(payload).setPageSize(pageSize).build(); + } + + @Override + public Integer extractPageSize(ListLogsRequest payload) { + return payload.getPageSize(); + } + + @Override + public Object extractNextToken(ListLogsResponse payload) { + return payload.getNextPageToken(); + } + + @Override + public Iterable extractResources(ListLogsResponse payload) { + return payload.getLogNamesList(); + } + }; + private static final PagedListResponseFactory< ListLogEntriesRequest, ListLogEntriesResponse, ListLogEntriesPagedResponse> LIST_LOG_ENTRIES_PAGE_STR_FACT = @@ -309,6 +355,19 @@ public ListMonitoredResourceDescriptorsPagedResponse createPagedListResponse( } }; + private static final PagedListResponseFactory< + ListLogsRequest, ListLogsResponse, ListLogsPagedResponse> + LIST_LOGS_PAGE_STR_FACT = + new PagedListResponseFactory() { + @Override + public ListLogsPagedResponse createPagedListResponse( + UnaryCallable callable, + ListLogsRequest request, + CallContext context) { + return new ListLogsPagedResponse(callable, LIST_LOGS_PAGE_STR_DESC, request, context); + } + }; + /** Builder for LoggingServiceV2Settings. */ public static class Builder extends ClientSettings.Builder { private final ImmutableList unaryMethodSettingsBuilders; @@ -323,6 +382,9 @@ public static class Builder extends ClientSettings.Builder { ListMonitoredResourceDescriptorsRequest, ListMonitoredResourceDescriptorsResponse, ListMonitoredResourceDescriptorsPagedResponse> listMonitoredResourceDescriptorsSettings; + private final PagedCallSettings.Builder< + ListLogsRequest, ListLogsResponse, ListLogsPagedResponse> + listLogsSettings; private static final ImmutableMap> RETRYABLE_CODE_DEFINITIONS; @@ -382,12 +444,17 @@ private Builder() { LoggingServiceV2Grpc.METHOD_LIST_MONITORED_RESOURCE_DESCRIPTORS, LIST_MONITORED_RESOURCE_DESCRIPTORS_PAGE_STR_FACT); + listLogsSettings = + PagedCallSettings.newBuilder( + LoggingServiceV2Grpc.METHOD_LIST_LOGS, LIST_LOGS_PAGE_STR_FACT); + unaryMethodSettingsBuilders = ImmutableList.of( deleteLogSettings, writeLogEntriesSettings, listLogEntriesSettings, - listMonitoredResourceDescriptorsSettings); + listMonitoredResourceDescriptorsSettings, + listLogsSettings); } private static Builder createDefault() { @@ -413,6 +480,11 @@ private static Builder createDefault() { .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("idempotent")) .setRetrySettingsBuilder(RETRY_PARAM_DEFINITIONS.get("default")); + builder + .listLogsSettings() + .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("idempotent")) + .setRetrySettingsBuilder(RETRY_PARAM_DEFINITIONS.get("default")); + return builder; } @@ -424,13 +496,15 @@ private Builder(LoggingServiceV2Settings settings) { listLogEntriesSettings = settings.listLogEntriesSettings.toBuilder(); listMonitoredResourceDescriptorsSettings = settings.listMonitoredResourceDescriptorsSettings.toBuilder(); + listLogsSettings = settings.listLogsSettings.toBuilder(); unaryMethodSettingsBuilders = ImmutableList.of( deleteLogSettings, writeLogEntriesSettings, listLogEntriesSettings, - listMonitoredResourceDescriptorsSettings); + listMonitoredResourceDescriptorsSettings, + listLogsSettings); } @Override @@ -483,6 +557,12 @@ public SimpleCallSettings.Builder deleteLogSettings() { return listMonitoredResourceDescriptorsSettings; } + /** Returns the builder for the settings used for calls to listLogs. */ + public PagedCallSettings.Builder + listLogsSettings() { + return listLogsSettings; + } + @Override public LoggingServiceV2Settings build() throws IOException { return new LoggingServiceV2Settings(this); diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/PagedResponseWrappers.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/PagedResponseWrappers.java index 60e880bca655..30ac77a1cd25 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/PagedResponseWrappers.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/PagedResponseWrappers.java @@ -24,6 +24,8 @@ import com.google.logging.v2.ListLogEntriesResponse; import com.google.logging.v2.ListLogMetricsRequest; import com.google.logging.v2.ListLogMetricsResponse; +import com.google.logging.v2.ListLogsRequest; +import com.google.logging.v2.ListLogsResponse; import com.google.logging.v2.ListMonitoredResourceDescriptorsRequest; import com.google.logging.v2.ListMonitoredResourceDescriptorsResponse; import com.google.logging.v2.ListSinksRequest; @@ -75,6 +77,18 @@ public ListMonitoredResourceDescriptorsPagedResponse( } } + public static class ListLogsPagedResponse + extends PagedListResponseImpl { + + public ListLogsPagedResponse( + UnaryCallable callable, + PagedListDescriptor pageDescriptor, + ListLogsRequest request, + CallContext context) { + super(callable, pageDescriptor, request, context); + } + } + public static class ListSinksPagedResponse extends PagedListResponseImpl { diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Test.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Test.java index c12e838da738..a2f10062c3bb 100644 --- a/google-cloud-logging/src/test/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Test.java +++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Test.java @@ -16,6 +16,7 @@ package com.google.cloud.logging.spi.v2; import static com.google.cloud.logging.spi.v2.PagedResponseWrappers.ListLogEntriesPagedResponse; +import static com.google.cloud.logging.spi.v2.PagedResponseWrappers.ListLogsPagedResponse; import com.google.api.MonitoredResource; import com.google.api.gax.grpc.ApiException; @@ -25,9 +26,13 @@ import com.google.logging.v2.DeleteLogRequest; import com.google.logging.v2.ListLogEntriesRequest; import com.google.logging.v2.ListLogEntriesResponse; +import com.google.logging.v2.ListLogsRequest; +import com.google.logging.v2.ListLogsResponse; import com.google.logging.v2.LogEntry; import com.google.logging.v2.LogName; import com.google.logging.v2.LogNameOneof; +import com.google.logging.v2.ParentNameOneof; +import com.google.logging.v2.ProjectName; import com.google.logging.v2.WriteLogEntriesRequest; import com.google.logging.v2.WriteLogEntriesResponse; import com.google.protobuf.Empty; @@ -215,4 +220,48 @@ public void listLogEntriesExceptionTest() throws Exception { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); } } + + @Test + @SuppressWarnings("all") + public void listLogsTest() { + String nextPageToken = ""; + String logNamesElement = "logNamesElement-1079688374"; + List logNames = Arrays.asList(logNamesElement); + ListLogsResponse expectedResponse = + ListLogsResponse.newBuilder() + .setNextPageToken(nextPageToken) + .addAllLogNames(logNames) + .build(); + mockLoggingServiceV2.addResponse(expectedResponse); + + ParentNameOneof parent = ParentNameOneof.from(ProjectName.create("[PROJECT]")); + + ListLogsPagedResponse pagedListResponse = client.listLogs(parent); + + List resources = Lists.newArrayList(pagedListResponse.iterateAllElements()); + Assert.assertEquals(1, resources.size()); + Assert.assertEquals(expectedResponse.getLogNamesList().get(0), resources.get(0)); + + List actualRequests = mockLoggingServiceV2.getRequests(); + Assert.assertEquals(1, actualRequests.size()); + ListLogsRequest actualRequest = (ListLogsRequest) actualRequests.get(0); + + Assert.assertEquals(parent, actualRequest.getParentAsParentNameOneof()); + } + + @Test + @SuppressWarnings("all") + public void listLogsExceptionTest() throws Exception { + StatusRuntimeException exception = new StatusRuntimeException(Status.INTERNAL); + mockLoggingServiceV2.addException(exception); + + try { + ParentNameOneof parent = ParentNameOneof.from(ProjectName.create("[PROJECT]")); + + client.listLogs(parent); + Assert.fail("No exception raised"); + } catch (ApiException e) { + Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); + } + } } diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/spi/v2/MockLoggingServiceV2Impl.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/spi/v2/MockLoggingServiceV2Impl.java index c6775b8908bb..02090c9934d4 100644 --- a/google-cloud-logging/src/test/java/com/google/cloud/logging/spi/v2/MockLoggingServiceV2Impl.java +++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/spi/v2/MockLoggingServiceV2Impl.java @@ -18,6 +18,8 @@ import com.google.logging.v2.DeleteLogRequest; import com.google.logging.v2.ListLogEntriesRequest; import com.google.logging.v2.ListLogEntriesResponse; +import com.google.logging.v2.ListLogsRequest; +import com.google.logging.v2.ListLogsResponse; import com.google.logging.v2.ListMonitoredResourceDescriptorsRequest; import com.google.logging.v2.ListMonitoredResourceDescriptorsResponse; import com.google.logging.v2.LoggingServiceV2Grpc.LoggingServiceV2ImplBase; @@ -121,4 +123,18 @@ public void listMonitoredResourceDescriptors( responseObserver.onError(new IllegalArgumentException("Unrecognized response type")); } } + + @Override + public void listLogs(ListLogsRequest request, StreamObserver responseObserver) { + Object response = responses.remove(); + if (response instanceof ListLogsResponse) { + requests.add(request); + responseObserver.onNext((ListLogsResponse) response); + responseObserver.onCompleted(); + } else if (response instanceof Exception) { + responseObserver.onError((Exception) response); + } else { + responseObserver.onError(new IllegalArgumentException("Unrecognized response type")); + } + } } diff --git a/google-cloud-monitoring/pom.xml b/google-cloud-monitoring/pom.xml index be83a529ed1b..ff4c86b3f52d 100644 --- a/google-cloud-monitoring/pom.xml +++ b/google-cloud-monitoring/pom.xml @@ -30,7 +30,7 @@ com.google.api.grpc grpc-google-cloud-monitoring-v3 - 0.1.3 + 0.1.5 io.grpc diff --git a/google-cloud-monitoring/src/main/java/com/google/cloud/monitoring/spi/v3/GroupServiceClient.java b/google-cloud-monitoring/src/main/java/com/google/cloud/monitoring/spi/v3/GroupServiceClient.java index d1f05562d37b..c927f6dc3ee2 100644 --- a/google-cloud-monitoring/src/main/java/com/google/cloud/monitoring/spi/v3/GroupServiceClient.java +++ b/google-cloud-monitoring/src/main/java/com/google/cloud/monitoring/spi/v3/GroupServiceClient.java @@ -20,15 +20,16 @@ import com.google.api.gax.grpc.ChannelAndExecutor; import com.google.api.gax.grpc.UnaryCallable; -import com.google.api.gax.protobuf.PathTemplate; import com.google.monitoring.v3.CreateGroupRequest; import com.google.monitoring.v3.DeleteGroupRequest; import com.google.monitoring.v3.GetGroupRequest; import com.google.monitoring.v3.Group; +import com.google.monitoring.v3.GroupName; import com.google.monitoring.v3.ListGroupMembersRequest; import com.google.monitoring.v3.ListGroupMembersResponse; import com.google.monitoring.v3.ListGroupsRequest; import com.google.monitoring.v3.ListGroupsResponse; +import com.google.monitoring.v3.ProjectName; import com.google.monitoring.v3.UpdateGroupRequest; import com.google.protobuf.Empty; import com.google.protobuf.ExperimentalApi; @@ -58,8 +59,8 @@ *
  * 
  * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
- *   String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
- *   Group response = groupServiceClient.getGroup(formattedName);
+ *   GroupName name = GroupName.create("[PROJECT]", "[GROUP]");
+ *   Group response = groupServiceClient.getGroup(name);
  * }
  * 
  * 
@@ -122,39 +123,6 @@ public class GroupServiceClient implements AutoCloseable { private final UnaryCallable listGroupMembersPagedCallable; - private static final PathTemplate PROJECT_PATH_TEMPLATE = - PathTemplate.createWithoutUrlEncoding("projects/{project}"); - - private static final PathTemplate GROUP_PATH_TEMPLATE = - PathTemplate.createWithoutUrlEncoding("projects/{project}/groups/{group}"); - - /** Formats a string containing the fully-qualified path to represent a project resource. */ - public static final String formatProjectName(String project) { - return PROJECT_PATH_TEMPLATE.instantiate("project", project); - } - - /** Formats a string containing the fully-qualified path to represent a group resource. */ - public static final String formatGroupName(String project, String group) { - return GROUP_PATH_TEMPLATE.instantiate( - "project", project, - "group", group); - } - - /** Parses the project from the given fully-qualified path which represents a project resource. */ - public static final String parseProjectFromProjectName(String projectName) { - return PROJECT_PATH_TEMPLATE.parse(projectName).get("project"); - } - - /** Parses the project from the given fully-qualified path which represents a group resource. */ - public static final String parseProjectFromGroupName(String groupName) { - return GROUP_PATH_TEMPLATE.parse(groupName).get("project"); - } - - /** Parses the group from the given fully-qualified path which represents a group resource. */ - public static final String parseGroupFromGroupName(String groupName) { - return GROUP_PATH_TEMPLATE.parse(groupName).get("group"); - } - /** Constructs an instance of GroupServiceClient with default settings. */ public static final GroupServiceClient create() throws IOException { return create(GroupServiceSettings.defaultBuilder().build()); @@ -229,9 +197,9 @@ public final GroupServiceSettings getSettings() { * *

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   ListGroupsRequest request = ListGroupsRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .build();
    *   for (Group element : groupServiceClient.listGroups(request).iterateAllElements()) {
    *     // doThingsWith(element);
@@ -254,9 +222,9 @@ public final ListGroupsPagedResponse listGroups(ListGroupsRequest request) {
    *
    * 

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   ListGroupsRequest request = ListGroupsRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .build();
    *   ListenableFuture<ListGroupsPagedResponse> future = groupServiceClient.listGroupsPagedCallable().futureCall(request);
    *   // Do something
@@ -278,9 +246,9 @@ public final UnaryCallable listGroup
    *
    * 

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   ListGroupsRequest request = ListGroupsRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .build();
    *   while (true) {
    *     ListGroupsResponse response = groupServiceClient.listGroupsCallable().call(request);
@@ -309,8 +277,8 @@ public final UnaryCallable listGroupsCall
    *
    * 

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
-   *   Group response = groupServiceClient.getGroup(formattedName);
+   *   GroupName name = GroupName.create("[PROJECT]", "[GROUP]");
+   *   Group response = groupServiceClient.getGroup(name);
    * }
    * 
* @@ -318,9 +286,9 @@ public final UnaryCallable listGroupsCall * `"projects/{project_id_or_number}/groups/{group_id}"`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ - public final Group getGroup(String name) { - GROUP_PATH_TEMPLATE.validate(name, "getGroup"); - GetGroupRequest request = GetGroupRequest.newBuilder().setName(name).build(); + public final Group getGroup(GroupName name) { + + GetGroupRequest request = GetGroupRequest.newBuilder().setNameWithGroupName(name).build(); return getGroup(request); } @@ -332,9 +300,9 @@ public final Group getGroup(String name) { * *

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
+   *   GroupName name = GroupName.create("[PROJECT]", "[GROUP]");
    *   GetGroupRequest request = GetGroupRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithGroupName(name)
    *     .build();
    *   Group response = groupServiceClient.getGroup(request);
    * }
@@ -355,9 +323,9 @@ private final Group getGroup(GetGroupRequest request) {
    *
    * 

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
+   *   GroupName name = GroupName.create("[PROJECT]", "[GROUP]");
    *   GetGroupRequest request = GetGroupRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithGroupName(name)
    *     .build();
    *   ListenableFuture<Group> future = groupServiceClient.getGroupCallable().futureCall(request);
    *   // Do something
@@ -377,9 +345,9 @@ public final UnaryCallable getGroupCallable() {
    *
    * 

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   Group group = Group.newBuilder().build();
-   *   Group response = groupServiceClient.createGroup(formattedName, group);
+   *   Group response = groupServiceClient.createGroup(name, group);
    * }
    * 
* @@ -389,10 +357,10 @@ public final UnaryCallable getGroupCallable() { * assigns the name. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ - public final Group createGroup(String name, Group group) { - PROJECT_PATH_TEMPLATE.validate(name, "createGroup"); + public final Group createGroup(ProjectName name, Group group) { + CreateGroupRequest request = - CreateGroupRequest.newBuilder().setName(name).setGroup(group).build(); + CreateGroupRequest.newBuilder().setNameWithProjectName(name).setGroup(group).build(); return createGroup(request); } @@ -404,10 +372,10 @@ public final Group createGroup(String name, Group group) { * *

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   Group group = Group.newBuilder().build();
    *   CreateGroupRequest request = CreateGroupRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .setGroup(group)
    *     .build();
    *   Group response = groupServiceClient.createGroup(request);
@@ -429,10 +397,10 @@ public final Group createGroup(CreateGroupRequest request) {
    *
    * 

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   Group group = Group.newBuilder().build();
    *   CreateGroupRequest request = CreateGroupRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .setGroup(group)
    *     .build();
    *   ListenableFuture<Group> future = groupServiceClient.createGroupCallable().futureCall(request);
@@ -521,8 +489,8 @@ public final UnaryCallable updateGroupCallable() {
    *
    * 

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
-   *   groupServiceClient.deleteGroup(formattedName);
+   *   GroupName name = GroupName.create("[PROJECT]", "[GROUP]");
+   *   groupServiceClient.deleteGroup(name);
    * }
    * 
* @@ -530,9 +498,9 @@ public final UnaryCallable updateGroupCallable() { * `"projects/{project_id_or_number}/groups/{group_id}"`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ - public final void deleteGroup(String name) { - GROUP_PATH_TEMPLATE.validate(name, "deleteGroup"); - DeleteGroupRequest request = DeleteGroupRequest.newBuilder().setName(name).build(); + public final void deleteGroup(GroupName name) { + + DeleteGroupRequest request = DeleteGroupRequest.newBuilder().setNameWithGroupName(name).build(); deleteGroup(request); } @@ -544,9 +512,9 @@ public final void deleteGroup(String name) { * *

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
+   *   GroupName name = GroupName.create("[PROJECT]", "[GROUP]");
    *   DeleteGroupRequest request = DeleteGroupRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithGroupName(name)
    *     .build();
    *   groupServiceClient.deleteGroup(request);
    * }
@@ -567,9 +535,9 @@ private final void deleteGroup(DeleteGroupRequest request) {
    *
    * 

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
+   *   GroupName name = GroupName.create("[PROJECT]", "[GROUP]");
    *   DeleteGroupRequest request = DeleteGroupRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithGroupName(name)
    *     .build();
    *   ListenableFuture<Void> future = groupServiceClient.deleteGroupCallable().futureCall(request);
    *   // Do something
@@ -589,8 +557,8 @@ public final UnaryCallable deleteGroupCallable() {
    *
    * 

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
-   *   for (MonitoredResource element : groupServiceClient.listGroupMembers(formattedName).iterateAllElements()) {
+   *   GroupName name = GroupName.create("[PROJECT]", "[GROUP]");
+   *   for (MonitoredResource element : groupServiceClient.listGroupMembers(name).iterateAllElements()) {
    *     // doThingsWith(element);
    *   }
    * }
@@ -600,9 +568,9 @@ public final UnaryCallable deleteGroupCallable() {
    *     `"projects/{project_id_or_number}/groups/{group_id}"`.
    * @throws com.google.api.gax.grpc.ApiException if the remote call fails
    */
-  public final ListGroupMembersPagedResponse listGroupMembers(String name) {
-    GROUP_PATH_TEMPLATE.validate(name, "listGroupMembers");
-    ListGroupMembersRequest request = ListGroupMembersRequest.newBuilder().setName(name).build();
+  public final ListGroupMembersPagedResponse listGroupMembers(GroupName name) {
+    ListGroupMembersRequest request =
+        ListGroupMembersRequest.newBuilder().setNameWithGroupName(name).build();
     return listGroupMembers(request);
   }
 
@@ -614,9 +582,9 @@ public final ListGroupMembersPagedResponse listGroupMembers(String name) {
    *
    * 

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
+   *   GroupName name = GroupName.create("[PROJECT]", "[GROUP]");
    *   ListGroupMembersRequest request = ListGroupMembersRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithGroupName(name)
    *     .build();
    *   for (MonitoredResource element : groupServiceClient.listGroupMembers(request).iterateAllElements()) {
    *     // doThingsWith(element);
@@ -639,9 +607,9 @@ public final ListGroupMembersPagedResponse listGroupMembers(ListGroupMembersRequ
    *
    * 

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
+   *   GroupName name = GroupName.create("[PROJECT]", "[GROUP]");
    *   ListGroupMembersRequest request = ListGroupMembersRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithGroupName(name)
    *     .build();
    *   ListenableFuture<ListGroupMembersPagedResponse> future = groupServiceClient.listGroupMembersPagedCallable().futureCall(request);
    *   // Do something
@@ -664,9 +632,9 @@ public final ListGroupMembersPagedResponse listGroupMembers(ListGroupMembersRequ
    *
    * 

    * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
-   *   String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
+   *   GroupName name = GroupName.create("[PROJECT]", "[GROUP]");
    *   ListGroupMembersRequest request = ListGroupMembersRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithGroupName(name)
    *     .build();
    *   while (true) {
    *     ListGroupMembersResponse response = groupServiceClient.listGroupMembersCallable().call(request);
diff --git a/google-cloud-monitoring/src/main/java/com/google/cloud/monitoring/spi/v3/MetricServiceClient.java b/google-cloud-monitoring/src/main/java/com/google/cloud/monitoring/spi/v3/MetricServiceClient.java
index a3c34aec881a..7dbe8909633d 100644
--- a/google-cloud-monitoring/src/main/java/com/google/cloud/monitoring/spi/v3/MetricServiceClient.java
+++ b/google-cloud-monitoring/src/main/java/com/google/cloud/monitoring/spi/v3/MetricServiceClient.java
@@ -23,7 +23,6 @@
 import com.google.api.MonitoredResourceDescriptor;
 import com.google.api.gax.grpc.ChannelAndExecutor;
 import com.google.api.gax.grpc.UnaryCallable;
-import com.google.api.gax.protobuf.PathTemplate;
 import com.google.monitoring.v3.CreateMetricDescriptorRequest;
 import com.google.monitoring.v3.CreateTimeSeriesRequest;
 import com.google.monitoring.v3.DeleteMetricDescriptorRequest;
@@ -36,6 +35,9 @@
 import com.google.monitoring.v3.ListTimeSeriesRequest;
 import com.google.monitoring.v3.ListTimeSeriesRequest.TimeSeriesView;
 import com.google.monitoring.v3.ListTimeSeriesResponse;
+import com.google.monitoring.v3.MetricDescriptorName;
+import com.google.monitoring.v3.MonitoredResourceDescriptorName;
+import com.google.monitoring.v3.ProjectName;
 import com.google.monitoring.v3.TimeInterval;
 import com.google.monitoring.v3.TimeSeries;
 import com.google.protobuf.Empty;
@@ -59,8 +61,8 @@
  * 
  * 
  * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
- *   String formattedName = MetricServiceClient.formatMonitoredResourceDescriptorName("[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]");
- *   MonitoredResourceDescriptor response = metricServiceClient.getMonitoredResourceDescriptor(formattedName);
+ *   MonitoredResourceDescriptorName name = MonitoredResourceDescriptorName.create("[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]");
+ *   MonitoredResourceDescriptor response = metricServiceClient.getMonitoredResourceDescriptor(name);
  * }
  * 
  * 
@@ -134,86 +136,6 @@ public class MetricServiceClient implements AutoCloseable { listTimeSeriesPagedCallable; private final UnaryCallable createTimeSeriesCallable; - private static final PathTemplate PROJECT_PATH_TEMPLATE = - PathTemplate.createWithoutUrlEncoding("projects/{project}"); - - private static final PathTemplate METRIC_DESCRIPTOR_PATH_TEMPLATE = - PathTemplate.createWithoutUrlEncoding( - "projects/{project}/metricDescriptors/{metric_descriptor=**}"); - - private static final PathTemplate MONITORED_RESOURCE_DESCRIPTOR_PATH_TEMPLATE = - PathTemplate.createWithoutUrlEncoding( - "projects/{project}/monitoredResourceDescriptors/{monitored_resource_descriptor}"); - - /** Formats a string containing the fully-qualified path to represent a project resource. */ - public static final String formatProjectName(String project) { - return PROJECT_PATH_TEMPLATE.instantiate("project", project); - } - - /** - * Formats a string containing the fully-qualified path to represent a metric_descriptor resource. - */ - public static final String formatMetricDescriptorName(String project, String metricDescriptor) { - return METRIC_DESCRIPTOR_PATH_TEMPLATE.instantiate( - "project", project, - "metric_descriptor", metricDescriptor); - } - - /** - * Formats a string containing the fully-qualified path to represent a - * monitored_resource_descriptor resource. - */ - public static final String formatMonitoredResourceDescriptorName( - String project, String monitoredResourceDescriptor) { - return MONITORED_RESOURCE_DESCRIPTOR_PATH_TEMPLATE.instantiate( - "project", project, - "monitored_resource_descriptor", monitoredResourceDescriptor); - } - - /** Parses the project from the given fully-qualified path which represents a project resource. */ - public static final String parseProjectFromProjectName(String projectName) { - return PROJECT_PATH_TEMPLATE.parse(projectName).get("project"); - } - - /** - * Parses the project from the given fully-qualified path which represents a metric_descriptor - * resource. - */ - public static final String parseProjectFromMetricDescriptorName(String metricDescriptorName) { - return METRIC_DESCRIPTOR_PATH_TEMPLATE.parse(metricDescriptorName).get("project"); - } - - /** - * Parses the metric_descriptor from the given fully-qualified path which represents a - * metric_descriptor resource. - */ - public static final String parseMetricDescriptorFromMetricDescriptorName( - String metricDescriptorName) { - return METRIC_DESCRIPTOR_PATH_TEMPLATE.parse(metricDescriptorName).get("metric_descriptor"); - } - - /** - * Parses the project from the given fully-qualified path which represents a - * monitored_resource_descriptor resource. - */ - public static final String parseProjectFromMonitoredResourceDescriptorName( - String monitoredResourceDescriptorName) { - return MONITORED_RESOURCE_DESCRIPTOR_PATH_TEMPLATE - .parse(monitoredResourceDescriptorName) - .get("project"); - } - - /** - * Parses the monitored_resource_descriptor from the given fully-qualified path which represents a - * monitored_resource_descriptor resource. - */ - public static final String parseMonitoredResourceDescriptorFromMonitoredResourceDescriptorName( - String monitoredResourceDescriptorName) { - return MONITORED_RESOURCE_DESCRIPTOR_PATH_TEMPLATE - .parse(monitoredResourceDescriptorName) - .get("monitored_resource_descriptor"); - } - /** Constructs an instance of MetricServiceClient with default settings. */ public static final MetricServiceClient create() throws IOException { return create(MetricServiceSettings.defaultBuilder().build()); @@ -301,8 +223,8 @@ public final MetricServiceSettings getSettings() { * *

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
-   *   for (MonitoredResourceDescriptor element : metricServiceClient.listMonitoredResourceDescriptors(formattedName).iterateAllElements()) {
+   *   ProjectName name = ProjectName.create("[PROJECT]");
+   *   for (MonitoredResourceDescriptor element : metricServiceClient.listMonitoredResourceDescriptors(name).iterateAllElements()) {
    *     // doThingsWith(element);
    *   }
    * }
@@ -313,10 +235,9 @@ public final MetricServiceSettings getSettings() {
    * @throws com.google.api.gax.grpc.ApiException if the remote call fails
    */
   public final ListMonitoredResourceDescriptorsPagedResponse listMonitoredResourceDescriptors(
-      String name) {
-    PROJECT_PATH_TEMPLATE.validate(name, "listMonitoredResourceDescriptors");
+      ProjectName name) {
     ListMonitoredResourceDescriptorsRequest request =
-        ListMonitoredResourceDescriptorsRequest.newBuilder().setName(name).build();
+        ListMonitoredResourceDescriptorsRequest.newBuilder().setNameWithProjectName(name).build();
     return listMonitoredResourceDescriptors(request);
   }
 
@@ -329,9 +250,9 @@ public final ListMonitoredResourceDescriptorsPagedResponse listMonitoredResource
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   ListMonitoredResourceDescriptorsRequest request = ListMonitoredResourceDescriptorsRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .build();
    *   for (MonitoredResourceDescriptor element : metricServiceClient.listMonitoredResourceDescriptors(request).iterateAllElements()) {
    *     // doThingsWith(element);
@@ -356,9 +277,9 @@ public final ListMonitoredResourceDescriptorsPagedResponse listMonitoredResource
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   ListMonitoredResourceDescriptorsRequest request = ListMonitoredResourceDescriptorsRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .build();
    *   ListenableFuture<ListMonitoredResourceDescriptorsPagedResponse> future = metricServiceClient.listMonitoredResourceDescriptorsPagedCallable().futureCall(request);
    *   // Do something
@@ -383,9 +304,9 @@ public final ListMonitoredResourceDescriptorsPagedResponse listMonitoredResource
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   ListMonitoredResourceDescriptorsRequest request = ListMonitoredResourceDescriptorsRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .build();
    *   while (true) {
    *     ListMonitoredResourceDescriptorsResponse response = metricServiceClient.listMonitoredResourceDescriptorsCallable().call(request);
@@ -417,8 +338,8 @@ public final ListMonitoredResourceDescriptorsPagedResponse listMonitoredResource
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatMonitoredResourceDescriptorName("[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]");
-   *   MonitoredResourceDescriptor response = metricServiceClient.getMonitoredResourceDescriptor(formattedName);
+   *   MonitoredResourceDescriptorName name = MonitoredResourceDescriptorName.create("[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]");
+   *   MonitoredResourceDescriptor response = metricServiceClient.getMonitoredResourceDescriptor(name);
    * }
    * 
* @@ -427,10 +348,13 @@ public final ListMonitoredResourceDescriptorsPagedResponse listMonitoredResource * `{resource_type}` is a predefined type, such as `cloudsql_database`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ - public final MonitoredResourceDescriptor getMonitoredResourceDescriptor(String name) { - MONITORED_RESOURCE_DESCRIPTOR_PATH_TEMPLATE.validate(name, "getMonitoredResourceDescriptor"); + public final MonitoredResourceDescriptor getMonitoredResourceDescriptor( + MonitoredResourceDescriptorName name) { + GetMonitoredResourceDescriptorRequest request = - GetMonitoredResourceDescriptorRequest.newBuilder().setName(name).build(); + GetMonitoredResourceDescriptorRequest.newBuilder() + .setNameWithMonitoredResourceDescriptorName(name) + .build(); return getMonitoredResourceDescriptor(request); } @@ -443,9 +367,9 @@ public final MonitoredResourceDescriptor getMonitoredResourceDescriptor(String n * *

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatMonitoredResourceDescriptorName("[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]");
+   *   MonitoredResourceDescriptorName name = MonitoredResourceDescriptorName.create("[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]");
    *   GetMonitoredResourceDescriptorRequest request = GetMonitoredResourceDescriptorRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithMonitoredResourceDescriptorName(name)
    *     .build();
    *   MonitoredResourceDescriptor response = metricServiceClient.getMonitoredResourceDescriptor(request);
    * }
@@ -468,9 +392,9 @@ private final MonitoredResourceDescriptor getMonitoredResourceDescriptor(
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatMonitoredResourceDescriptorName("[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]");
+   *   MonitoredResourceDescriptorName name = MonitoredResourceDescriptorName.create("[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]");
    *   GetMonitoredResourceDescriptorRequest request = GetMonitoredResourceDescriptorRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithMonitoredResourceDescriptorName(name)
    *     .build();
    *   ListenableFuture<MonitoredResourceDescriptor> future = metricServiceClient.getMonitoredResourceDescriptorCallable().futureCall(request);
    *   // Do something
@@ -492,8 +416,8 @@ private final MonitoredResourceDescriptor getMonitoredResourceDescriptor(
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
-   *   for (MetricDescriptor element : metricServiceClient.listMetricDescriptors(formattedName).iterateAllElements()) {
+   *   ProjectName name = ProjectName.create("[PROJECT]");
+   *   for (MetricDescriptor element : metricServiceClient.listMetricDescriptors(name).iterateAllElements()) {
    *     // doThingsWith(element);
    *   }
    * }
@@ -503,10 +427,9 @@ private final MonitoredResourceDescriptor getMonitoredResourceDescriptor(
    *     `"projects/{project_id_or_number}"`.
    * @throws com.google.api.gax.grpc.ApiException if the remote call fails
    */
-  public final ListMetricDescriptorsPagedResponse listMetricDescriptors(String name) {
-    PROJECT_PATH_TEMPLATE.validate(name, "listMetricDescriptors");
+  public final ListMetricDescriptorsPagedResponse listMetricDescriptors(ProjectName name) {
     ListMetricDescriptorsRequest request =
-        ListMetricDescriptorsRequest.newBuilder().setName(name).build();
+        ListMetricDescriptorsRequest.newBuilder().setNameWithProjectName(name).build();
     return listMetricDescriptors(request);
   }
 
@@ -519,9 +442,9 @@ public final ListMetricDescriptorsPagedResponse listMetricDescriptors(String nam
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   ListMetricDescriptorsRequest request = ListMetricDescriptorsRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .build();
    *   for (MetricDescriptor element : metricServiceClient.listMetricDescriptors(request).iterateAllElements()) {
    *     // doThingsWith(element);
@@ -546,9 +469,9 @@ public final ListMetricDescriptorsPagedResponse listMetricDescriptors(
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   ListMetricDescriptorsRequest request = ListMetricDescriptorsRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .build();
    *   ListenableFuture<ListMetricDescriptorsPagedResponse> future = metricServiceClient.listMetricDescriptorsPagedCallable().futureCall(request);
    *   // Do something
@@ -572,9 +495,9 @@ public final ListMetricDescriptorsPagedResponse listMetricDescriptors(
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   ListMetricDescriptorsRequest request = ListMetricDescriptorsRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .build();
    *   while (true) {
    *     ListMetricDescriptorsResponse response = metricServiceClient.listMetricDescriptorsCallable().call(request);
@@ -604,8 +527,8 @@ public final ListMetricDescriptorsPagedResponse listMetricDescriptors(
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatMetricDescriptorName("[PROJECT]", "[METRIC_DESCRIPTOR]");
-   *   MetricDescriptor response = metricServiceClient.getMetricDescriptor(formattedName);
+   *   MetricDescriptorName name = MetricDescriptorName.create("[PROJECT]", "[METRIC_DESCRIPTOR]");
+   *   MetricDescriptor response = metricServiceClient.getMetricDescriptor(name);
    * }
    * 
* @@ -614,10 +537,10 @@ public final ListMetricDescriptorsPagedResponse listMetricDescriptors( * `{metric_id}` is `"compute.googleapis.com/instance/disk/read_bytes_count"`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ - public final MetricDescriptor getMetricDescriptor(String name) { - METRIC_DESCRIPTOR_PATH_TEMPLATE.validate(name, "getMetricDescriptor"); + public final MetricDescriptor getMetricDescriptor(MetricDescriptorName name) { + GetMetricDescriptorRequest request = - GetMetricDescriptorRequest.newBuilder().setName(name).build(); + GetMetricDescriptorRequest.newBuilder().setNameWithMetricDescriptorName(name).build(); return getMetricDescriptor(request); } @@ -629,9 +552,9 @@ public final MetricDescriptor getMetricDescriptor(String name) { * *

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatMetricDescriptorName("[PROJECT]", "[METRIC_DESCRIPTOR]");
+   *   MetricDescriptorName name = MetricDescriptorName.create("[PROJECT]", "[METRIC_DESCRIPTOR]");
    *   GetMetricDescriptorRequest request = GetMetricDescriptorRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithMetricDescriptorName(name)
    *     .build();
    *   MetricDescriptor response = metricServiceClient.getMetricDescriptor(request);
    * }
@@ -652,9 +575,9 @@ private final MetricDescriptor getMetricDescriptor(GetMetricDescriptorRequest re
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatMetricDescriptorName("[PROJECT]", "[METRIC_DESCRIPTOR]");
+   *   MetricDescriptorName name = MetricDescriptorName.create("[PROJECT]", "[METRIC_DESCRIPTOR]");
    *   GetMetricDescriptorRequest request = GetMetricDescriptorRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithMetricDescriptorName(name)
    *     .build();
    *   ListenableFuture<MetricDescriptor> future = metricServiceClient.getMetricDescriptorCallable().futureCall(request);
    *   // Do something
@@ -676,9 +599,9 @@ private final MetricDescriptor getMetricDescriptor(GetMetricDescriptorRequest re
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   MetricDescriptor metricDescriptor = MetricDescriptor.newBuilder().build();
-   *   MetricDescriptor response = metricServiceClient.createMetricDescriptor(formattedName, metricDescriptor);
+   *   MetricDescriptor response = metricServiceClient.createMetricDescriptor(name, metricDescriptor);
    * }
    * 
* @@ -688,11 +611,11 @@ private final MetricDescriptor getMetricDescriptor(GetMetricDescriptorRequest re * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final MetricDescriptor createMetricDescriptor( - String name, MetricDescriptor metricDescriptor) { - PROJECT_PATH_TEMPLATE.validate(name, "createMetricDescriptor"); + ProjectName name, MetricDescriptor metricDescriptor) { + CreateMetricDescriptorRequest request = CreateMetricDescriptorRequest.newBuilder() - .setName(name) + .setNameWithProjectName(name) .setMetricDescriptor(metricDescriptor) .build(); return createMetricDescriptor(request); @@ -707,10 +630,10 @@ public final MetricDescriptor createMetricDescriptor( * *

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   MetricDescriptor metricDescriptor = MetricDescriptor.newBuilder().build();
    *   CreateMetricDescriptorRequest request = CreateMetricDescriptorRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .setMetricDescriptor(metricDescriptor)
    *     .build();
    *   MetricDescriptor response = metricServiceClient.createMetricDescriptor(request);
@@ -733,10 +656,10 @@ public final MetricDescriptor createMetricDescriptor(CreateMetricDescriptorReque
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   MetricDescriptor metricDescriptor = MetricDescriptor.newBuilder().build();
    *   CreateMetricDescriptorRequest request = CreateMetricDescriptorRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .setMetricDescriptor(metricDescriptor)
    *     .build();
    *   ListenableFuture<MetricDescriptor> future = metricServiceClient.createMetricDescriptorCallable().futureCall(request);
@@ -759,8 +682,8 @@ public final MetricDescriptor createMetricDescriptor(CreateMetricDescriptorReque
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatMetricDescriptorName("[PROJECT]", "[METRIC_DESCRIPTOR]");
-   *   metricServiceClient.deleteMetricDescriptor(formattedName);
+   *   MetricDescriptorName name = MetricDescriptorName.create("[PROJECT]", "[METRIC_DESCRIPTOR]");
+   *   metricServiceClient.deleteMetricDescriptor(name);
    * }
    * 
* @@ -769,10 +692,10 @@ public final MetricDescriptor createMetricDescriptor(CreateMetricDescriptorReque * `{metric_id}` is: `"custom.googleapis.com/my_test_metric"`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ - public final void deleteMetricDescriptor(String name) { - METRIC_DESCRIPTOR_PATH_TEMPLATE.validate(name, "deleteMetricDescriptor"); + public final void deleteMetricDescriptor(MetricDescriptorName name) { + DeleteMetricDescriptorRequest request = - DeleteMetricDescriptorRequest.newBuilder().setName(name).build(); + DeleteMetricDescriptorRequest.newBuilder().setNameWithMetricDescriptorName(name).build(); deleteMetricDescriptor(request); } @@ -785,9 +708,9 @@ public final void deleteMetricDescriptor(String name) { * *

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatMetricDescriptorName("[PROJECT]", "[METRIC_DESCRIPTOR]");
+   *   MetricDescriptorName name = MetricDescriptorName.create("[PROJECT]", "[METRIC_DESCRIPTOR]");
    *   DeleteMetricDescriptorRequest request = DeleteMetricDescriptorRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithMetricDescriptorName(name)
    *     .build();
    *   metricServiceClient.deleteMetricDescriptor(request);
    * }
@@ -809,9 +732,9 @@ private final void deleteMetricDescriptor(DeleteMetricDescriptorRequest request)
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatMetricDescriptorName("[PROJECT]", "[METRIC_DESCRIPTOR]");
+   *   MetricDescriptorName name = MetricDescriptorName.create("[PROJECT]", "[METRIC_DESCRIPTOR]");
    *   DeleteMetricDescriptorRequest request = DeleteMetricDescriptorRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithMetricDescriptorName(name)
    *     .build();
    *   ListenableFuture<Void> future = metricServiceClient.deleteMetricDescriptorCallable().futureCall(request);
    *   // Do something
@@ -832,11 +755,11 @@ private final void deleteMetricDescriptor(DeleteMetricDescriptorRequest request)
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   String filter = "";
    *   TimeInterval interval = TimeInterval.newBuilder().build();
    *   ListTimeSeriesRequest.TimeSeriesView view = ListTimeSeriesRequest.TimeSeriesView.FULL;
-   *   for (TimeSeries element : metricServiceClient.listTimeSeries(formattedName, filter, interval, view).iterateAllElements()) {
+   *   for (TimeSeries element : metricServiceClient.listTimeSeries(name, filter, interval, view).iterateAllElements()) {
    *     // doThingsWith(element);
    *   }
    * }
@@ -855,14 +778,13 @@ private final void deleteMetricDescriptor(DeleteMetricDescriptorRequest request)
    * @throws com.google.api.gax.grpc.ApiException if the remote call fails
    */
   public final ListTimeSeriesPagedResponse listTimeSeries(
-      String name,
+      ProjectName name,
       String filter,
       TimeInterval interval,
       ListTimeSeriesRequest.TimeSeriesView view) {
-    PROJECT_PATH_TEMPLATE.validate(name, "listTimeSeries");
     ListTimeSeriesRequest request =
         ListTimeSeriesRequest.newBuilder()
-            .setName(name)
+            .setNameWithProjectName(name)
             .setFilter(filter)
             .setInterval(interval)
             .setView(view)
@@ -878,12 +800,12 @@ public final ListTimeSeriesPagedResponse listTimeSeries(
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   String filter = "";
    *   TimeInterval interval = TimeInterval.newBuilder().build();
    *   ListTimeSeriesRequest.TimeSeriesView view = ListTimeSeriesRequest.TimeSeriesView.FULL;
    *   ListTimeSeriesRequest request = ListTimeSeriesRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .setFilter(filter)
    *     .setInterval(interval)
    *     .setView(view)
@@ -909,12 +831,12 @@ public final ListTimeSeriesPagedResponse listTimeSeries(ListTimeSeriesRequest re
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   String filter = "";
    *   TimeInterval interval = TimeInterval.newBuilder().build();
    *   ListTimeSeriesRequest.TimeSeriesView view = ListTimeSeriesRequest.TimeSeriesView.FULL;
    *   ListTimeSeriesRequest request = ListTimeSeriesRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .setFilter(filter)
    *     .setInterval(interval)
    *     .setView(view)
@@ -940,12 +862,12 @@ public final ListTimeSeriesPagedResponse listTimeSeries(ListTimeSeriesRequest re
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   String filter = "";
    *   TimeInterval interval = TimeInterval.newBuilder().build();
    *   ListTimeSeriesRequest.TimeSeriesView view = ListTimeSeriesRequest.TimeSeriesView.FULL;
    *   ListTimeSeriesRequest request = ListTimeSeriesRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .setFilter(filter)
    *     .setInterval(interval)
    *     .setView(view)
@@ -980,9 +902,9 @@ public final ListTimeSeriesPagedResponse listTimeSeries(ListTimeSeriesRequest re
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   List<TimeSeries> timeSeries = new ArrayList<>();
-   *   metricServiceClient.createTimeSeries(formattedName, timeSeries);
+   *   metricServiceClient.createTimeSeries(name, timeSeries);
    * }
    * 
* @@ -994,10 +916,13 @@ public final ListTimeSeriesPagedResponse listTimeSeries(ListTimeSeriesRequest re * by supplying all label values for the metric and the monitored resource. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ - public final void createTimeSeries(String name, List timeSeries) { - PROJECT_PATH_TEMPLATE.validate(name, "createTimeSeries"); + public final void createTimeSeries(ProjectName name, List timeSeries) { + CreateTimeSeriesRequest request = - CreateTimeSeriesRequest.newBuilder().setName(name).addAllTimeSeries(timeSeries).build(); + CreateTimeSeriesRequest.newBuilder() + .setNameWithProjectName(name) + .addAllTimeSeries(timeSeries) + .build(); createTimeSeries(request); } @@ -1011,10 +936,10 @@ public final void createTimeSeries(String name, List timeSeries) { * *

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   List<TimeSeries> timeSeries = new ArrayList<>();
    *   CreateTimeSeriesRequest request = CreateTimeSeriesRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .addAllTimeSeries(timeSeries)
    *     .build();
    *   metricServiceClient.createTimeSeries(request);
@@ -1038,10 +963,10 @@ public final void createTimeSeries(CreateTimeSeriesRequest request) {
    *
    * 

    * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
-   *   String formattedName = MetricServiceClient.formatProjectName("[PROJECT]");
+   *   ProjectName name = ProjectName.create("[PROJECT]");
    *   List<TimeSeries> timeSeries = new ArrayList<>();
    *   CreateTimeSeriesRequest request = CreateTimeSeriesRequest.newBuilder()
-   *     .setName(formattedName)
+   *     .setNameWithProjectName(name)
    *     .addAllTimeSeries(timeSeries)
    *     .build();
    *   ListenableFuture<Void> future = metricServiceClient.createTimeSeriesCallable().futureCall(request);
diff --git a/google-cloud-monitoring/src/main/java/com/google/cloud/monitoring/spi/v3/package-info.java b/google-cloud-monitoring/src/main/java/com/google/cloud/monitoring/spi/v3/package-info.java
index ee65c23d2533..95db6ed368b8 100644
--- a/google-cloud-monitoring/src/main/java/com/google/cloud/monitoring/spi/v3/package-info.java
+++ b/google-cloud-monitoring/src/main/java/com/google/cloud/monitoring/spi/v3/package-info.java
@@ -36,8 +36,8 @@
  * 
  * 
  * try (GroupServiceClient groupServiceClient = GroupServiceClient.create()) {
- *   String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]");
- *   Group response = groupServiceClient.getGroup(formattedName);
+ *   GroupName name = GroupName.create("[PROJECT]", "[GROUP]");
+ *   Group response = groupServiceClient.getGroup(name);
  * }
  * 
  * 
@@ -52,8 +52,8 @@ *
  * 
  * try (MetricServiceClient metricServiceClient = MetricServiceClient.create()) {
- *   String formattedName = MetricServiceClient.formatMonitoredResourceDescriptorName("[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]");
- *   MonitoredResourceDescriptor response = metricServiceClient.getMonitoredResourceDescriptor(formattedName);
+ *   MonitoredResourceDescriptorName name = MonitoredResourceDescriptorName.create("[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]");
+ *   MonitoredResourceDescriptor response = metricServiceClient.getMonitoredResourceDescriptor(name);
  * }
  * 
  * 
diff --git a/google-cloud-monitoring/src/test/java/com/google/cloud/monitoring/spi/v3/GroupServiceTest.java b/google-cloud-monitoring/src/test/java/com/google/cloud/monitoring/spi/v3/GroupServiceTest.java index 638dce922115..b4194bfa0fb1 100644 --- a/google-cloud-monitoring/src/test/java/com/google/cloud/monitoring/spi/v3/GroupServiceTest.java +++ b/google-cloud-monitoring/src/test/java/com/google/cloud/monitoring/spi/v3/GroupServiceTest.java @@ -26,8 +26,10 @@ import com.google.monitoring.v3.DeleteGroupRequest; import com.google.monitoring.v3.GetGroupRequest; import com.google.monitoring.v3.Group; +import com.google.monitoring.v3.GroupName; import com.google.monitoring.v3.ListGroupMembersRequest; import com.google.monitoring.v3.ListGroupMembersResponse; +import com.google.monitoring.v3.ProjectName; import com.google.monitoring.v3.UpdateGroupRequest; import com.google.protobuf.Empty; import com.google.protobuf.GeneratedMessageV3; @@ -83,31 +85,31 @@ public void tearDown() throws Exception { @Test @SuppressWarnings("all") public void getGroupTest() { - String formattedName2 = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]"); + GroupName name2 = GroupName.create("[PROJECT]", "[GROUP]"); String displayName = "displayName1615086568"; - String parentName = "parentName1015022848"; + GroupName parentName = GroupName.create("[PROJECT]", "[GROUP]"); String filter = "filter-1274492040"; boolean isCluster = false; Group expectedResponse = Group.newBuilder() - .setName(formattedName2) + .setNameWithGroupName(name2) .setDisplayName(displayName) - .setParentName(parentName) + .setParentNameWithGroupName(parentName) .setFilter(filter) .setIsCluster(isCluster) .build(); mockGroupService.addResponse(expectedResponse); - String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]"); + GroupName name = GroupName.create("[PROJECT]", "[GROUP]"); - Group actualResponse = client.getGroup(formattedName); + Group actualResponse = client.getGroup(name); Assert.assertEquals(expectedResponse, actualResponse); List actualRequests = mockGroupService.getRequests(); Assert.assertEquals(1, actualRequests.size()); GetGroupRequest actualRequest = (GetGroupRequest) actualRequests.get(0); - Assert.assertEquals(formattedName, actualRequest.getName()); + Assert.assertEquals(name, actualRequest.getNameAsGroupName()); } @Test @@ -117,9 +119,9 @@ public void getGroupExceptionTest() throws Exception { mockGroupService.addException(exception); try { - String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]"); + GroupName name = GroupName.create("[PROJECT]", "[GROUP]"); - client.getGroup(formattedName); + client.getGroup(name); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); @@ -129,32 +131,32 @@ public void getGroupExceptionTest() throws Exception { @Test @SuppressWarnings("all") public void createGroupTest() { - String formattedName2 = GroupServiceClient.formatProjectName("[PROJECT]"); + GroupName name2 = GroupName.create("[PROJECT]", "[GROUP]"); String displayName = "displayName1615086568"; - String parentName = "parentName1015022848"; + GroupName parentName = GroupName.create("[PROJECT]", "[GROUP]"); String filter = "filter-1274492040"; boolean isCluster = false; Group expectedResponse = Group.newBuilder() - .setName(formattedName2) + .setNameWithGroupName(name2) .setDisplayName(displayName) - .setParentName(parentName) + .setParentNameWithGroupName(parentName) .setFilter(filter) .setIsCluster(isCluster) .build(); mockGroupService.addResponse(expectedResponse); - String formattedName = GroupServiceClient.formatProjectName("[PROJECT]"); + ProjectName name = ProjectName.create("[PROJECT]"); Group group = Group.newBuilder().build(); - Group actualResponse = client.createGroup(formattedName, group); + Group actualResponse = client.createGroup(name, group); Assert.assertEquals(expectedResponse, actualResponse); List actualRequests = mockGroupService.getRequests(); Assert.assertEquals(1, actualRequests.size()); CreateGroupRequest actualRequest = (CreateGroupRequest) actualRequests.get(0); - Assert.assertEquals(formattedName, actualRequest.getName()); + Assert.assertEquals(name, actualRequest.getNameAsProjectName()); Assert.assertEquals(group, actualRequest.getGroup()); } @@ -165,10 +167,10 @@ public void createGroupExceptionTest() throws Exception { mockGroupService.addException(exception); try { - String formattedName = GroupServiceClient.formatProjectName("[PROJECT]"); + ProjectName name = ProjectName.create("[PROJECT]"); Group group = Group.newBuilder().build(); - client.createGroup(formattedName, group); + client.createGroup(name, group); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); @@ -178,16 +180,16 @@ public void createGroupExceptionTest() throws Exception { @Test @SuppressWarnings("all") public void updateGroupTest() { - String name = "name3373707"; + GroupName name = GroupName.create("[PROJECT]", "[GROUP]"); String displayName = "displayName1615086568"; - String parentName = "parentName1015022848"; + GroupName parentName = GroupName.create("[PROJECT]", "[GROUP]"); String filter = "filter-1274492040"; boolean isCluster = false; Group expectedResponse = Group.newBuilder() - .setName(name) + .setNameWithGroupName(name) .setDisplayName(displayName) - .setParentName(parentName) + .setParentNameWithGroupName(parentName) .setFilter(filter) .setIsCluster(isCluster) .build(); @@ -227,15 +229,15 @@ public void deleteGroupTest() { Empty expectedResponse = Empty.newBuilder().build(); mockGroupService.addResponse(expectedResponse); - String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]"); + GroupName name = GroupName.create("[PROJECT]", "[GROUP]"); - client.deleteGroup(formattedName); + client.deleteGroup(name); List actualRequests = mockGroupService.getRequests(); Assert.assertEquals(1, actualRequests.size()); DeleteGroupRequest actualRequest = (DeleteGroupRequest) actualRequests.get(0); - Assert.assertEquals(formattedName, actualRequest.getName()); + Assert.assertEquals(name, actualRequest.getNameAsGroupName()); } @Test @@ -245,9 +247,9 @@ public void deleteGroupExceptionTest() throws Exception { mockGroupService.addException(exception); try { - String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]"); + GroupName name = GroupName.create("[PROJECT]", "[GROUP]"); - client.deleteGroup(formattedName); + client.deleteGroup(name); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); @@ -269,9 +271,9 @@ public void listGroupMembersTest() { .build(); mockGroupService.addResponse(expectedResponse); - String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]"); + GroupName name = GroupName.create("[PROJECT]", "[GROUP]"); - ListGroupMembersPagedResponse pagedListResponse = client.listGroupMembers(formattedName); + ListGroupMembersPagedResponse pagedListResponse = client.listGroupMembers(name); List resources = Lists.newArrayList(pagedListResponse.iterateAllElements()); Assert.assertEquals(1, resources.size()); @@ -281,7 +283,7 @@ public void listGroupMembersTest() { Assert.assertEquals(1, actualRequests.size()); ListGroupMembersRequest actualRequest = (ListGroupMembersRequest) actualRequests.get(0); - Assert.assertEquals(formattedName, actualRequest.getName()); + Assert.assertEquals(name, actualRequest.getNameAsGroupName()); } @Test @@ -291,9 +293,9 @@ public void listGroupMembersExceptionTest() throws Exception { mockGroupService.addException(exception); try { - String formattedName = GroupServiceClient.formatGroupName("[PROJECT]", "[GROUP]"); + GroupName name = GroupName.create("[PROJECT]", "[GROUP]"); - client.listGroupMembers(formattedName); + client.listGroupMembers(name); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); diff --git a/google-cloud-monitoring/src/test/java/com/google/cloud/monitoring/spi/v3/MetricServiceTest.java b/google-cloud-monitoring/src/test/java/com/google/cloud/monitoring/spi/v3/MetricServiceTest.java index 49a93045749b..ec9fcee28fff 100644 --- a/google-cloud-monitoring/src/test/java/com/google/cloud/monitoring/spi/v3/MetricServiceTest.java +++ b/google-cloud-monitoring/src/test/java/com/google/cloud/monitoring/spi/v3/MetricServiceTest.java @@ -37,6 +37,9 @@ import com.google.monitoring.v3.ListTimeSeriesRequest; import com.google.monitoring.v3.ListTimeSeriesRequest.TimeSeriesView; import com.google.monitoring.v3.ListTimeSeriesResponse; +import com.google.monitoring.v3.MetricDescriptorName; +import com.google.monitoring.v3.MonitoredResourceDescriptorName; +import com.google.monitoring.v3.ProjectName; import com.google.monitoring.v3.TimeInterval; import com.google.monitoring.v3.TimeSeries; import com.google.protobuf.Empty; @@ -106,10 +109,10 @@ public void listMonitoredResourceDescriptorsTest() { .build(); mockMetricService.addResponse(expectedResponse); - String formattedName = MetricServiceClient.formatProjectName("[PROJECT]"); + ProjectName name = ProjectName.create("[PROJECT]"); ListMonitoredResourceDescriptorsPagedResponse pagedListResponse = - client.listMonitoredResourceDescriptors(formattedName); + client.listMonitoredResourceDescriptors(name); List resources = Lists.newArrayList(pagedListResponse.iterateAllElements()); @@ -121,7 +124,7 @@ public void listMonitoredResourceDescriptorsTest() { ListMonitoredResourceDescriptorsRequest actualRequest = (ListMonitoredResourceDescriptorsRequest) actualRequests.get(0); - Assert.assertEquals(formattedName, actualRequest.getName()); + Assert.assertEquals(name, actualRequest.getNameAsProjectName()); } @Test @@ -131,9 +134,9 @@ public void listMonitoredResourceDescriptorsExceptionTest() throws Exception { mockMetricService.addException(exception); try { - String formattedName = MetricServiceClient.formatProjectName("[PROJECT]"); + ProjectName name = ProjectName.create("[PROJECT]"); - client.listMonitoredResourceDescriptors(formattedName); + client.listMonitoredResourceDescriptors(name); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); @@ -143,27 +146,23 @@ public void listMonitoredResourceDescriptorsExceptionTest() throws Exception { @Test @SuppressWarnings("all") public void getMonitoredResourceDescriptorTest() { - String formattedName2 = - MetricServiceClient.formatMonitoredResourceDescriptorName( - "[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]"); + String name2 = "name2-1052831874"; String type = "type3575610"; String displayName = "displayName1615086568"; String description = "description-1724546052"; MonitoredResourceDescriptor expectedResponse = MonitoredResourceDescriptor.newBuilder() - .setName(formattedName2) + .setName(name2) .setType(type) .setDisplayName(displayName) .setDescription(description) .build(); mockMetricService.addResponse(expectedResponse); - String formattedName = - MetricServiceClient.formatMonitoredResourceDescriptorName( - "[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]"); + MonitoredResourceDescriptorName name = + MonitoredResourceDescriptorName.create("[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]"); - MonitoredResourceDescriptor actualResponse = - client.getMonitoredResourceDescriptor(formattedName); + MonitoredResourceDescriptor actualResponse = client.getMonitoredResourceDescriptor(name); Assert.assertEquals(expectedResponse, actualResponse); List actualRequests = mockMetricService.getRequests(); @@ -171,7 +170,7 @@ public void getMonitoredResourceDescriptorTest() { GetMonitoredResourceDescriptorRequest actualRequest = (GetMonitoredResourceDescriptorRequest) actualRequests.get(0); - Assert.assertEquals(formattedName, actualRequest.getName()); + Assert.assertEquals(name, actualRequest.getNameAsMonitoredResourceDescriptorName()); } @Test @@ -181,11 +180,10 @@ public void getMonitoredResourceDescriptorExceptionTest() throws Exception { mockMetricService.addException(exception); try { - String formattedName = - MetricServiceClient.formatMonitoredResourceDescriptorName( - "[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]"); + MonitoredResourceDescriptorName name = + MonitoredResourceDescriptorName.create("[PROJECT]", "[MONITORED_RESOURCE_DESCRIPTOR]"); - client.getMonitoredResourceDescriptor(formattedName); + client.getMonitoredResourceDescriptor(name); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); @@ -205,10 +203,9 @@ public void listMetricDescriptorsTest() { .build(); mockMetricService.addResponse(expectedResponse); - String formattedName = MetricServiceClient.formatProjectName("[PROJECT]"); + ProjectName name = ProjectName.create("[PROJECT]"); - ListMetricDescriptorsPagedResponse pagedListResponse = - client.listMetricDescriptors(formattedName); + ListMetricDescriptorsPagedResponse pagedListResponse = client.listMetricDescriptors(name); List resources = Lists.newArrayList(pagedListResponse.iterateAllElements()); Assert.assertEquals(1, resources.size()); @@ -219,7 +216,7 @@ public void listMetricDescriptorsTest() { ListMetricDescriptorsRequest actualRequest = (ListMetricDescriptorsRequest) actualRequests.get(0); - Assert.assertEquals(formattedName, actualRequest.getName()); + Assert.assertEquals(name, actualRequest.getNameAsProjectName()); } @Test @@ -229,9 +226,9 @@ public void listMetricDescriptorsExceptionTest() throws Exception { mockMetricService.addException(exception); try { - String formattedName = MetricServiceClient.formatProjectName("[PROJECT]"); + ProjectName name = ProjectName.create("[PROJECT]"); - client.listMetricDescriptors(formattedName); + client.listMetricDescriptors(name); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); @@ -241,15 +238,14 @@ public void listMetricDescriptorsExceptionTest() throws Exception { @Test @SuppressWarnings("all") public void getMetricDescriptorTest() { - String formattedName2 = - MetricServiceClient.formatMetricDescriptorName("[PROJECT]", "[METRIC_DESCRIPTOR]"); + String name2 = "name2-1052831874"; String type = "type3575610"; String unit = "unit3594628"; String description = "description-1724546052"; String displayName = "displayName1615086568"; MetricDescriptor expectedResponse = MetricDescriptor.newBuilder() - .setName(formattedName2) + .setName(name2) .setType(type) .setUnit(unit) .setDescription(description) @@ -257,17 +253,16 @@ public void getMetricDescriptorTest() { .build(); mockMetricService.addResponse(expectedResponse); - String formattedName = - MetricServiceClient.formatMetricDescriptorName("[PROJECT]", "[METRIC_DESCRIPTOR]"); + MetricDescriptorName name = MetricDescriptorName.create("[PROJECT]", "[METRIC_DESCRIPTOR]"); - MetricDescriptor actualResponse = client.getMetricDescriptor(formattedName); + MetricDescriptor actualResponse = client.getMetricDescriptor(name); Assert.assertEquals(expectedResponse, actualResponse); List actualRequests = mockMetricService.getRequests(); Assert.assertEquals(1, actualRequests.size()); GetMetricDescriptorRequest actualRequest = (GetMetricDescriptorRequest) actualRequests.get(0); - Assert.assertEquals(formattedName, actualRequest.getName()); + Assert.assertEquals(name, actualRequest.getNameAsMetricDescriptorName()); } @Test @@ -277,10 +272,9 @@ public void getMetricDescriptorExceptionTest() throws Exception { mockMetricService.addException(exception); try { - String formattedName = - MetricServiceClient.formatMetricDescriptorName("[PROJECT]", "[METRIC_DESCRIPTOR]"); + MetricDescriptorName name = MetricDescriptorName.create("[PROJECT]", "[METRIC_DESCRIPTOR]"); - client.getMetricDescriptor(formattedName); + client.getMetricDescriptor(name); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); @@ -290,14 +284,14 @@ public void getMetricDescriptorExceptionTest() throws Exception { @Test @SuppressWarnings("all") public void createMetricDescriptorTest() { - String formattedName2 = MetricServiceClient.formatProjectName("[PROJECT]"); + String name2 = "name2-1052831874"; String type = "type3575610"; String unit = "unit3594628"; String description = "description-1724546052"; String displayName = "displayName1615086568"; MetricDescriptor expectedResponse = MetricDescriptor.newBuilder() - .setName(formattedName2) + .setName(name2) .setType(type) .setUnit(unit) .setDescription(description) @@ -305,11 +299,10 @@ public void createMetricDescriptorTest() { .build(); mockMetricService.addResponse(expectedResponse); - String formattedName = MetricServiceClient.formatProjectName("[PROJECT]"); + ProjectName name = ProjectName.create("[PROJECT]"); MetricDescriptor metricDescriptor = MetricDescriptor.newBuilder().build(); - MetricDescriptor actualResponse = - client.createMetricDescriptor(formattedName, metricDescriptor); + MetricDescriptor actualResponse = client.createMetricDescriptor(name, metricDescriptor); Assert.assertEquals(expectedResponse, actualResponse); List actualRequests = mockMetricService.getRequests(); @@ -317,7 +310,7 @@ public void createMetricDescriptorTest() { CreateMetricDescriptorRequest actualRequest = (CreateMetricDescriptorRequest) actualRequests.get(0); - Assert.assertEquals(formattedName, actualRequest.getName()); + Assert.assertEquals(name, actualRequest.getNameAsProjectName()); Assert.assertEquals(metricDescriptor, actualRequest.getMetricDescriptor()); } @@ -328,10 +321,10 @@ public void createMetricDescriptorExceptionTest() throws Exception { mockMetricService.addException(exception); try { - String formattedName = MetricServiceClient.formatProjectName("[PROJECT]"); + ProjectName name = ProjectName.create("[PROJECT]"); MetricDescriptor metricDescriptor = MetricDescriptor.newBuilder().build(); - client.createMetricDescriptor(formattedName, metricDescriptor); + client.createMetricDescriptor(name, metricDescriptor); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); @@ -344,17 +337,16 @@ public void deleteMetricDescriptorTest() { Empty expectedResponse = Empty.newBuilder().build(); mockMetricService.addResponse(expectedResponse); - String formattedName = - MetricServiceClient.formatMetricDescriptorName("[PROJECT]", "[METRIC_DESCRIPTOR]"); + MetricDescriptorName name = MetricDescriptorName.create("[PROJECT]", "[METRIC_DESCRIPTOR]"); - client.deleteMetricDescriptor(formattedName); + client.deleteMetricDescriptor(name); List actualRequests = mockMetricService.getRequests(); Assert.assertEquals(1, actualRequests.size()); DeleteMetricDescriptorRequest actualRequest = (DeleteMetricDescriptorRequest) actualRequests.get(0); - Assert.assertEquals(formattedName, actualRequest.getName()); + Assert.assertEquals(name, actualRequest.getNameAsMetricDescriptorName()); } @Test @@ -364,10 +356,9 @@ public void deleteMetricDescriptorExceptionTest() throws Exception { mockMetricService.addException(exception); try { - String formattedName = - MetricServiceClient.formatMetricDescriptorName("[PROJECT]", "[METRIC_DESCRIPTOR]"); + MetricDescriptorName name = MetricDescriptorName.create("[PROJECT]", "[METRIC_DESCRIPTOR]"); - client.deleteMetricDescriptor(formattedName); + client.deleteMetricDescriptor(name); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); @@ -387,13 +378,13 @@ public void listTimeSeriesTest() { .build(); mockMetricService.addResponse(expectedResponse); - String formattedName = MetricServiceClient.formatProjectName("[PROJECT]"); + ProjectName name = ProjectName.create("[PROJECT]"); String filter = "filter-1274492040"; TimeInterval interval = TimeInterval.newBuilder().build(); ListTimeSeriesRequest.TimeSeriesView view = ListTimeSeriesRequest.TimeSeriesView.FULL; ListTimeSeriesPagedResponse pagedListResponse = - client.listTimeSeries(formattedName, filter, interval, view); + client.listTimeSeries(name, filter, interval, view); List resources = Lists.newArrayList(pagedListResponse.iterateAllElements()); Assert.assertEquals(1, resources.size()); @@ -403,7 +394,7 @@ public void listTimeSeriesTest() { Assert.assertEquals(1, actualRequests.size()); ListTimeSeriesRequest actualRequest = (ListTimeSeriesRequest) actualRequests.get(0); - Assert.assertEquals(formattedName, actualRequest.getName()); + Assert.assertEquals(name, actualRequest.getNameAsProjectName()); Assert.assertEquals(filter, actualRequest.getFilter()); Assert.assertEquals(interval, actualRequest.getInterval()); Assert.assertEquals(view, actualRequest.getView()); @@ -416,12 +407,12 @@ public void listTimeSeriesExceptionTest() throws Exception { mockMetricService.addException(exception); try { - String formattedName = MetricServiceClient.formatProjectName("[PROJECT]"); + ProjectName name = ProjectName.create("[PROJECT]"); String filter = "filter-1274492040"; TimeInterval interval = TimeInterval.newBuilder().build(); ListTimeSeriesRequest.TimeSeriesView view = ListTimeSeriesRequest.TimeSeriesView.FULL; - client.listTimeSeries(formattedName, filter, interval, view); + client.listTimeSeries(name, filter, interval, view); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); @@ -434,16 +425,16 @@ public void createTimeSeriesTest() { Empty expectedResponse = Empty.newBuilder().build(); mockMetricService.addResponse(expectedResponse); - String formattedName = MetricServiceClient.formatProjectName("[PROJECT]"); + ProjectName name = ProjectName.create("[PROJECT]"); List timeSeries = new ArrayList<>(); - client.createTimeSeries(formattedName, timeSeries); + client.createTimeSeries(name, timeSeries); List actualRequests = mockMetricService.getRequests(); Assert.assertEquals(1, actualRequests.size()); CreateTimeSeriesRequest actualRequest = (CreateTimeSeriesRequest) actualRequests.get(0); - Assert.assertEquals(formattedName, actualRequest.getName()); + Assert.assertEquals(name, actualRequest.getNameAsProjectName()); Assert.assertEquals(timeSeries, actualRequest.getTimeSeriesList()); } @@ -454,10 +445,10 @@ public void createTimeSeriesExceptionTest() throws Exception { mockMetricService.addException(exception); try { - String formattedName = MetricServiceClient.formatProjectName("[PROJECT]"); + ProjectName name = ProjectName.create("[PROJECT]"); List timeSeries = new ArrayList<>(); - client.createTimeSeries(formattedName, timeSeries); + client.createTimeSeries(name, timeSeries); Assert.fail("No exception raised"); } catch (ApiException e) { Assert.assertEquals(Status.INTERNAL.getCode(), e.getStatusCode()); diff --git a/google-cloud-pubsub/pom.xml b/google-cloud-pubsub/pom.xml index 3d268c6771da..e780732e4b2c 100644 --- a/google-cloud-pubsub/pom.xml +++ b/google-cloud-pubsub/pom.xml @@ -30,7 +30,7 @@ com.google.api.grpc grpc-google-cloud-pubsub-v1 - 0.1.3 + 0.1.5 io.grpc diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/MessageConsumerImpl.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/MessageConsumerImpl.java index 889b415bd685..7d8f22ad2acc 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/MessageConsumerImpl.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/MessageConsumerImpl.java @@ -16,7 +16,6 @@ package com.google.cloud.pubsub; -import static com.google.cloud.pubsub.spi.v1.SubscriberClient.formatSubscriptionName; import static com.google.common.base.MoreObjects.firstNonNull; import com.google.cloud.GrpcServiceOptions.ExecutorFactory; @@ -28,6 +27,7 @@ import com.google.pubsub.v1.PullRequest; import com.google.pubsub.v1.PullResponse; +import com.google.pubsub.v1.SubscriptionName; import io.grpc.internal.SharedResourceHolder; import java.util.List; @@ -154,7 +154,8 @@ public void failure(Throwable error) { private PullRequest createPullRequest() { return PullRequest.newBuilder() - .setSubscription(formatSubscriptionName(pubsubOptions.getProjectId(), subscription)) + .setSubscriptionWithSubscriptionName( + SubscriptionName.create(pubsubOptions.getProjectId(), subscription)) .setMaxMessages(maxQueuedCallbacks - queuedCallbacks.get()) .setReturnImmediately(false) .build(); diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java index 4e4307b6108d..35dff5ab2524 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java @@ -31,8 +31,6 @@ import com.google.cloud.Policy; import com.google.cloud.pubsub.spi.PubSubRpc; import com.google.cloud.pubsub.spi.PubSubRpc.PullFuture; -import com.google.cloud.pubsub.spi.v1.PublisherClient; -import com.google.cloud.pubsub.spi.v1.SubscriberClient; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Throwables; @@ -62,11 +60,13 @@ import com.google.pubsub.v1.ListTopicsResponse; import com.google.pubsub.v1.ModifyAckDeadlineRequest; import com.google.pubsub.v1.ModifyPushConfigRequest; +import com.google.pubsub.v1.ProjectName; import com.google.pubsub.v1.PublishRequest; import com.google.pubsub.v1.PublishResponse; import com.google.pubsub.v1.PullRequest; import com.google.pubsub.v1.PullResponse; - +import com.google.pubsub.v1.SubscriptionName; +import com.google.pubsub.v1.TopicName; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -247,7 +247,7 @@ public Topic getTopic(String topic) { @Override public Future getTopicAsync(String topic) { GetTopicRequest request = GetTopicRequest.newBuilder() - .setTopic(PublisherClient.formatTopicName(getOptions().getProjectId(), topic)) + .setTopicWithTopicName(TopicName.create(getOptions().getProjectId(), topic)) .build(); return transform(rpc.get(request), Topic.fromPbFunction(this)); } @@ -260,7 +260,7 @@ public boolean deleteTopic(String topic) { @Override public Future deleteTopicAsync(String topic) { DeleteTopicRequest request = DeleteTopicRequest.newBuilder() - .setTopic(PublisherClient.formatTopicName(getOptions().getProjectId(), topic)) + .setTopicWithTopicName(TopicName.create(getOptions().getProjectId(), topic)) .build(); return transform(rpc.delete(request), EMPTY_TO_BOOLEAN_FUNCTION); } @@ -268,7 +268,7 @@ public Future deleteTopicAsync(String topic) { private static ListTopicsRequest listTopicsRequest(PubSubOptions serviceOptions, Map options) { ListTopicsRequest.Builder builder = ListTopicsRequest.newBuilder(); - builder.setProject(SubscriberClient.formatProjectName(serviceOptions.getProjectId())); + builder.setProjectWithProjectName(ProjectName.create(serviceOptions.getProjectId())); Integer pageSize = PAGE_SIZE.get(options); String pageToken = PAGE_TOKEN.get(options); if (pageSize != null) { @@ -316,7 +316,7 @@ public String publish(String topic, Message message) { private static PublishRequest publishRequest(PubSubOptions serviceOptions, String topic, Iterable messages) { PublishRequest.Builder builder = PublishRequest.newBuilder(); - builder.setTopic(PublisherClient.formatTopicName(serviceOptions.getProjectId(), topic)); + builder.setTopicWithTopicName(TopicName.create(serviceOptions.getProjectId(), topic)); builder.addAllMessages(Iterables.transform(messages, Message.TO_PB_FUNCTION)); return builder.build(); } @@ -378,8 +378,8 @@ public Subscription getSubscription(String subscription) { @Override public Future getSubscriptionAsync(String subscription) { GetSubscriptionRequest request = GetSubscriptionRequest.newBuilder() - .setSubscription( - SubscriberClient.formatSubscriptionName(getOptions().getProjectId(), subscription)) + .setSubscriptionWithSubscriptionName( + SubscriptionName.create(getOptions().getProjectId(), subscription)) .build(); return transform(rpc.get(request), Subscription.fromPbFunction(this)); } @@ -392,8 +392,8 @@ public void replacePushConfig(String subscription, PushConfig pushConfig) { @Override public Future replacePushConfigAsync(String subscription, PushConfig pushConfig) { ModifyPushConfigRequest request = ModifyPushConfigRequest.newBuilder() - .setSubscription( - SubscriberClient.formatSubscriptionName(getOptions().getProjectId(), subscription)) + .setSubscriptionWithSubscriptionName( + SubscriptionName.create(getOptions().getProjectId(), subscription)) .setPushConfig(pushConfig != null ? pushConfig.toPb() : com.google.pubsub.v1.PushConfig.getDefaultInstance()) .build(); @@ -408,8 +408,8 @@ public boolean deleteSubscription(String subscription) { @Override public Future deleteSubscriptionAsync(String subscription) { DeleteSubscriptionRequest request = DeleteSubscriptionRequest.newBuilder() - .setSubscription( - SubscriberClient.formatSubscriptionName(getOptions().getProjectId(), subscription)) + .setSubscriptionWithSubscriptionName( + SubscriptionName.create(getOptions().getProjectId(), subscription)) .build(); return transform(rpc.delete(request), EMPTY_TO_BOOLEAN_FUNCTION); } @@ -417,7 +417,7 @@ public Future deleteSubscriptionAsync(String subscription) { private static ListSubscriptionsRequest listSubscriptionsRequest(PubSubOptions serviceOptions, Map options) { ListSubscriptionsRequest.Builder builder = ListSubscriptionsRequest.newBuilder(); - builder.setProject(SubscriberClient.formatProjectName(serviceOptions.getProjectId())); + builder.setProjectWithProjectName(ProjectName.create(serviceOptions.getProjectId())); Integer pageSize = PAGE_SIZE.getInteger(options); String pageToken = PAGE_TOKEN.getString(options); if (pageSize != null) { @@ -460,7 +460,7 @@ public Future> listSubscriptionsAsync(ListOption... opti private static ListTopicSubscriptionsRequest listSubscriptionsRequest(String topic, PubSubOptions serviceOptions, Map options) { ListTopicSubscriptionsRequest.Builder builder = ListTopicSubscriptionsRequest.newBuilder(); - builder.setTopic(PublisherClient.formatTopicName(serviceOptions.getProjectId(), topic)); + builder.setTopicWithTopicName(TopicName.create(serviceOptions.getProjectId(), topic)); Integer pageSize = PAGE_SIZE.getInteger(options); String pageToken = PAGE_TOKEN.getString(options); if (pageSize != null) { @@ -515,8 +515,8 @@ public Future> listSubscriptionsAsync(String topic, private Future> pullAsync(final String subscription, int maxMessages, boolean returnImmediately) { PullRequest request = PullRequest.newBuilder() - .setSubscription( - SubscriberClient.formatSubscriptionName(getOptions().getProjectId(), subscription)) + .setSubscriptionWithSubscriptionName( + SubscriptionName.create(getOptions().getProjectId(), subscription)) .setMaxMessages(maxMessages) .setReturnImmediately(returnImmediately) .build(); @@ -588,8 +588,8 @@ public void ack(String subscription, Iterable ackIds) { @Override public Future ackAsync(String subscription, Iterable ackIds) { AcknowledgeRequest request = AcknowledgeRequest.newBuilder() - .setSubscription( - SubscriberClient.formatSubscriptionName(getOptions().getProjectId(), subscription)) + .setSubscriptionWithSubscriptionName( + SubscriptionName.create(getOptions().getProjectId(), subscription)) .addAllAckIds(ackIds) .build(); return transform(rpc.acknowledge(request), EMPTY_TO_VOID_FUNCTION); @@ -637,8 +637,8 @@ public void modifyAckDeadline(String subscription, int deadline, TimeUnit unit, public Future modifyAckDeadlineAsync(String subscription, int deadline, TimeUnit unit, Iterable ackIds) { ModifyAckDeadlineRequest request = ModifyAckDeadlineRequest.newBuilder() - .setSubscription( - SubscriberClient.formatSubscriptionName(getOptions().getProjectId(), subscription)) + .setSubscriptionWithSubscriptionName( + SubscriptionName.create(getOptions().getProjectId(), subscription)) .setAckDeadlineSeconds((int) TimeUnit.SECONDS.convert(deadline, unit)) .addAllAckIds(ackIds) .build(); @@ -653,7 +653,7 @@ public Policy getTopicPolicy(String topic) { @Override public Future getTopicPolicyAsync(String topic) { return transform( - rpc.getIamPolicy(PublisherClient.formatTopicName(getOptions().getProjectId(), topic)), + rpc.getIamPolicy(TopicName.create(getOptions().getProjectId(), topic).toString()), POLICY_TO_PB_FUNCTION); } @@ -666,7 +666,7 @@ public Policy replaceTopicPolicy(String topic, Policy newPolicy) { public Future replaceTopicPolicyAsync(String topic, Policy newPolicy) { SetIamPolicyRequest request = SetIamPolicyRequest.newBuilder() .setPolicy(PolicyMarshaller.INSTANCE.toPb(newPolicy)) - .setResource(PublisherClient.formatTopicName(getOptions().getProjectId(), topic)) + .setResource(TopicName.create(getOptions().getProjectId(), topic).toString()) .build(); return transform(rpc.setIamPolicy(request), POLICY_TO_PB_FUNCTION); } @@ -679,7 +679,7 @@ public List testTopicPermissions(String topic, final List permi @Override public Future> testTopicPermissionsAsync(String topic, List permissions) { TestIamPermissionsRequest request = TestIamPermissionsRequest.newBuilder() - .setResource(PublisherClient.formatTopicName(getOptions().getProjectId(), topic)) + .setResource(TopicName.create(getOptions().getProjectId(), topic).toString()) .addAllPermissions(permissions) .build(); return transform(rpc.testIamPermissions(request), permissionsFromPbFunction(permissions)); @@ -694,7 +694,7 @@ public Policy getSubscriptionPolicy(String subscription) { public Future getSubscriptionPolicyAsync(String subscription) { return transform( rpc.getIamPolicy( - SubscriberClient.formatSubscriptionName(getOptions().getProjectId(), subscription)), + SubscriptionName.create(getOptions().getProjectId(), subscription).toString()), POLICY_TO_PB_FUNCTION); } @@ -708,7 +708,7 @@ public Future replaceSubscriptionPolicyAsync(String subscription, Policy SetIamPolicyRequest request = SetIamPolicyRequest.newBuilder() .setPolicy(PolicyMarshaller.INSTANCE.toPb(newPolicy)) .setResource( - SubscriberClient.formatSubscriptionName(getOptions().getProjectId(), subscription)) + SubscriptionName.create(getOptions().getProjectId(), subscription).toString()) .build(); return transform(rpc.setIamPolicy(request), POLICY_TO_PB_FUNCTION); } @@ -723,7 +723,7 @@ public Future> testSubscriptionPermissionsAsync(String subscriptio List permissions) { TestIamPermissionsRequest request = TestIamPermissionsRequest.newBuilder() .setResource( - SubscriberClient.formatSubscriptionName(getOptions().getProjectId(), subscription)) + SubscriptionName.create(getOptions().getProjectId(), subscription).toString()) .addAllPermissions(permissions) .build(); return transform(rpc.testIamPermissions(request), permissionsFromPbFunction(permissions)); diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/SubscriptionId.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/SubscriptionId.java index 8a3cc166efb6..a01fc9dfc79e 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/SubscriptionId.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/SubscriptionId.java @@ -16,12 +16,10 @@ package com.google.cloud.pubsub; -import static com.google.cloud.pubsub.spi.v1.SubscriberClient.parseProjectFromSubscriptionName; -import static com.google.cloud.pubsub.spi.v1.SubscriberClient.parseSubscriptionFromSubscriptionName; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.MoreObjects; - +import com.google.pubsub.v1.SubscriptionName; import java.io.Serializable; import java.util.Objects; @@ -99,7 +97,8 @@ public final boolean equals(Object obj) { } static SubscriptionId fromPb(String pb) { - return new SubscriptionId(parseProjectFromSubscriptionName(pb), - parseSubscriptionFromSubscriptionName(pb)); + SubscriptionName subscriptionName = SubscriptionName.parse(pb); + return new SubscriptionId(subscriptionName.getProject(), + subscriptionName.getSubscription()); } } diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/SubscriptionInfo.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/SubscriptionInfo.java index 5bbc4cd161bc..b5ff3106dbd0 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/SubscriptionInfo.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/SubscriptionInfo.java @@ -18,9 +18,8 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.cloud.pubsub.spi.v1.SubscriberClient; import com.google.common.base.MoreObjects; - +import com.google.pubsub.v1.SubscriptionName; import java.io.Serializable; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -394,7 +393,7 @@ com.google.pubsub.v1.Subscription toPb(String projectId) { com.google.pubsub.v1.Subscription.Builder builder = com.google.pubsub.v1.Subscription.newBuilder(); builder.setTopic(topic.toPb(projectId)); - builder.setName(SubscriberClient.formatSubscriptionName(projectId, name)); + builder.setNameWithSubscriptionName(SubscriptionName.create(projectId, name)); builder.setAckDeadlineSeconds(ackDeadlineSeconds); if (pushConfig != null) { builder.setPushConfig(pushConfig.toPb()); @@ -404,7 +403,7 @@ com.google.pubsub.v1.Subscription toPb(String projectId) { static SubscriptionInfo fromPb(com.google.pubsub.v1.Subscription subscription) { Builder builder = newBuilder(TopicId.fromPb(subscription.getTopic()), - SubscriberClient.parseSubscriptionFromSubscriptionName(subscription.getName())); + subscription.getNameAsSubscriptionName().getSubscription()); builder.setAckDeadLineSeconds(subscription.getAckDeadlineSeconds()); // A subscription with an "empty" push config is a pull subscription if (subscription.hasPushConfig() diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/TopicId.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/TopicId.java index b481c4572000..a121ca11c4e1 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/TopicId.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/TopicId.java @@ -16,13 +16,11 @@ package com.google.cloud.pubsub; -import static com.google.cloud.pubsub.spi.v1.PublisherClient.formatTopicName; -import static com.google.cloud.pubsub.spi.v1.PublisherClient.parseProjectFromTopicName; -import static com.google.cloud.pubsub.spi.v1.PublisherClient.parseTopicFromTopicName; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.MoreObjects; +import com.google.pubsub.v1.TopicName; import java.io.Serializable; import java.util.Objects; @@ -120,7 +118,7 @@ public boolean equals(Object obj) { } String toPb(String projectId) { - return formatTopicName(project != null ? project : projectId, topic); + return TopicName.create(project != null ? project : projectId, topic).toString(); } /** @@ -150,6 +148,7 @@ static TopicId fromPb(String pb) { if (Objects.equals(pb, DELETED_TOPIC_NAME)) { return DELETED_TOPIC; } - return TopicId.of(parseProjectFromTopicName(pb), parseTopicFromTopicName(pb)); + TopicName topicName = TopicName.parse(pb); + return TopicId.of(topicName.getProject(), topicName.getTopic()); } } diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/TopicInfo.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/TopicInfo.java index 75771d5ebb35..d669e1b94846 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/TopicInfo.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/TopicInfo.java @@ -18,9 +18,8 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.cloud.pubsub.spi.v1.PublisherClient; import com.google.common.base.MoreObjects; - +import com.google.pubsub.v1.TopicName; import java.io.Serializable; import java.util.Objects; @@ -145,12 +144,12 @@ public String toString() { com.google.pubsub.v1.Topic toPb(String projectId) { return com.google.pubsub.v1.Topic.newBuilder() - .setName(PublisherClient.formatTopicName(projectId, name)) + .setNameWithTopicName(TopicName.create(projectId, name)) .build(); } static TopicInfo fromPb(com.google.pubsub.v1.Topic topicPb) { - return newBuilder(PublisherClient.parseTopicFromTopicName(topicPb.getName())).build(); + return newBuilder(topicPb.getNameAsTopicName().getTopic()).build(); } /** diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PublisherClient.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PublisherClient.java index e1cd043a439f..059b93be6a73 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PublisherClient.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PublisherClient.java @@ -20,7 +20,6 @@ import com.google.api.gax.grpc.ChannelAndExecutor; import com.google.api.gax.grpc.UnaryCallable; -import com.google.api.gax.protobuf.PathTemplate; import com.google.iam.v1.GetIamPolicyRequest; import com.google.iam.v1.Policy; import com.google.iam.v1.SetIamPolicyRequest; @@ -127,39 +126,6 @@ public class PublisherClient implements AutoCloseable { private final UnaryCallable testIamPermissionsCallable; - private static final PathTemplate PROJECT_PATH_TEMPLATE = - PathTemplate.createWithoutUrlEncoding("projects/{project}"); - - private static final PathTemplate TOPIC_PATH_TEMPLATE = - PathTemplate.createWithoutUrlEncoding("projects/{project}/topics/{topic}"); - - /** Formats a string containing the fully-qualified path to represent a project resource. */ - public static final String formatProjectName(String project) { - return PROJECT_PATH_TEMPLATE.instantiate("project", project); - } - - /** Formats a string containing the fully-qualified path to represent a topic resource. */ - public static final String formatTopicName(String project, String topic) { - return TOPIC_PATH_TEMPLATE.instantiate( - "project", project, - "topic", topic); - } - - /** Parses the project from the given fully-qualified path which represents a project resource. */ - public static final String parseProjectFromProjectName(String projectName) { - return PROJECT_PATH_TEMPLATE.parse(projectName).get("project"); - } - - /** Parses the project from the given fully-qualified path which represents a topic resource. */ - public static final String parseProjectFromTopicName(String topicName) { - return TOPIC_PATH_TEMPLATE.parse(topicName).get("project"); - } - - /** Parses the topic from the given fully-qualified path which represents a topic resource. */ - public static final String parseTopicFromTopicName(String topicName) { - return TOPIC_PATH_TEMPLATE.parse(topicName).get("topic"); - } - /** Constructs an instance of PublisherClient with default settings. */ public static final PublisherClient create() throws IOException { return create(PublisherSettings.defaultBuilder().build()); @@ -327,7 +293,8 @@ public final UnaryCallable createTopicCallable() { * } *
* - * @param topic The messages in the request will be published on this topic. + * @param topic The messages in the request will be published on this topic. Format is + * `projects/{project}/topics/{topic}`. * @param messages The messages to publish. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ @@ -412,7 +379,7 @@ public final UnaryCallable publishCallable() { * } *
* - * @param topic The name of the topic to get. + * @param topic The name of the topic to get. Format is `projects/{project}/topics/{topic}`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final Topic getTopic(TopicName topic) { @@ -481,7 +448,8 @@ public final UnaryCallable getTopicCallable() { * } *
* - * @param project The name of the cloud project that topics belong to. + * @param project The name of the cloud project that topics belong to. Format is + * `projects/{project}`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final ListTopicsPagedResponse listTopics(ProjectName project) { @@ -585,7 +553,8 @@ public final UnaryCallable listTopicsCall * } *
* - * @param topic The name of the topic that subscriptions are attached to. + * @param topic The name of the topic that subscriptions are attached to. Format is + * `projects/{project}/topics/{topic}`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final ListTopicSubscriptionsPagedResponse listTopicSubscriptions(TopicName topic) { @@ -693,7 +662,7 @@ public final ListTopicSubscriptionsPagedResponse listTopicSubscriptions( * } *
* - * @param topic Name of the topic to delete. + * @param topic Name of the topic to delete. Format is `projects/{project}/topics/{topic}`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final void deleteTopic(TopicName topic) { @@ -762,7 +731,7 @@ public final UnaryCallable deleteTopicCallable() { * *

    * try (PublisherClient publisherClient = PublisherClient.create()) {
-   *   String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+   *   String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
    *   Policy policy = Policy.newBuilder().build();
    *   Policy response = publisherClient.setIamPolicy(formattedResource, policy);
    * }
@@ -777,7 +746,7 @@ public final UnaryCallable deleteTopicCallable() {
    * @throws com.google.api.gax.grpc.ApiException if the remote call fails
    */
   public final Policy setIamPolicy(String resource, Policy policy) {
-    TOPIC_PATH_TEMPLATE.validate(resource, "setIamPolicy");
+
     SetIamPolicyRequest request =
         SetIamPolicyRequest.newBuilder().setResource(resource).setPolicy(policy).build();
     return setIamPolicy(request);
@@ -791,7 +760,7 @@ public final Policy setIamPolicy(String resource, Policy policy) {
    *
    * 

    * try (PublisherClient publisherClient = PublisherClient.create()) {
-   *   String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+   *   String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
    *   Policy policy = Policy.newBuilder().build();
    *   SetIamPolicyRequest request = SetIamPolicyRequest.newBuilder()
    *     .setResource(formattedResource)
@@ -816,7 +785,7 @@ public final Policy setIamPolicy(SetIamPolicyRequest request) {
    *
    * 

    * try (PublisherClient publisherClient = PublisherClient.create()) {
-   *   String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+   *   String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
    *   Policy policy = Policy.newBuilder().build();
    *   SetIamPolicyRequest request = SetIamPolicyRequest.newBuilder()
    *     .setResource(formattedResource)
@@ -841,7 +810,7 @@ public final UnaryCallable setIamPolicyCallable() {
    *
    * 

    * try (PublisherClient publisherClient = PublisherClient.create()) {
-   *   String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+   *   String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
    *   Policy response = publisherClient.getIamPolicy(formattedResource);
    * }
    * 
@@ -852,7 +821,7 @@ public final UnaryCallable setIamPolicyCallable() { * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final Policy getIamPolicy(String resource) { - TOPIC_PATH_TEMPLATE.validate(resource, "getIamPolicy"); + GetIamPolicyRequest request = GetIamPolicyRequest.newBuilder().setResource(resource).build(); return getIamPolicy(request); } @@ -866,7 +835,7 @@ public final Policy getIamPolicy(String resource) { * *

    * try (PublisherClient publisherClient = PublisherClient.create()) {
-   *   String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+   *   String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
    *   GetIamPolicyRequest request = GetIamPolicyRequest.newBuilder()
    *     .setResource(formattedResource)
    *     .build();
@@ -890,7 +859,7 @@ private final Policy getIamPolicy(GetIamPolicyRequest request) {
    *
    * 

    * try (PublisherClient publisherClient = PublisherClient.create()) {
-   *   String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+   *   String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
    *   GetIamPolicyRequest request = GetIamPolicyRequest.newBuilder()
    *     .setResource(formattedResource)
    *     .build();
@@ -906,13 +875,14 @@ public final UnaryCallable getIamPolicyCallable() {
 
   // AUTO-GENERATED DOCUMENTATION AND METHOD
   /**
-   * Returns permissions that a caller has on the specified resource.
+   * Returns permissions that a caller has on the specified resource. If the resource does not
+   * exist, this will return an empty set of permissions, not a NOT_FOUND error.
    *
    * 

Sample code: * *


    * try (PublisherClient publisherClient = PublisherClient.create()) {
-   *   String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+   *   String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
    *   List<String> permissions = new ArrayList<>();
    *   TestIamPermissionsResponse response = publisherClient.testIamPermissions(formattedResource, permissions);
    * }
@@ -928,7 +898,7 @@ public final UnaryCallable getIamPolicyCallable() {
    */
   public final TestIamPermissionsResponse testIamPermissions(
       String resource, List permissions) {
-    TOPIC_PATH_TEMPLATE.validate(resource, "testIamPermissions");
+
     TestIamPermissionsRequest request =
         TestIamPermissionsRequest.newBuilder()
             .setResource(resource)
@@ -939,13 +909,14 @@ public final TestIamPermissionsResponse testIamPermissions(
 
   // AUTO-GENERATED DOCUMENTATION AND METHOD
   /**
-   * Returns permissions that a caller has on the specified resource.
+   * Returns permissions that a caller has on the specified resource. If the resource does not
+   * exist, this will return an empty set of permissions, not a NOT_FOUND error.
    *
    * 

Sample code: * *


    * try (PublisherClient publisherClient = PublisherClient.create()) {
-   *   String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+   *   String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
    *   List<String> permissions = new ArrayList<>();
    *   TestIamPermissionsRequest request = TestIamPermissionsRequest.newBuilder()
    *     .setResource(formattedResource)
@@ -964,13 +935,14 @@ public final TestIamPermissionsResponse testIamPermissions(TestIamPermissionsReq
 
   // AUTO-GENERATED DOCUMENTATION AND METHOD
   /**
-   * Returns permissions that a caller has on the specified resource.
+   * Returns permissions that a caller has on the specified resource. If the resource does not
+   * exist, this will return an empty set of permissions, not a NOT_FOUND error.
    *
    * 

Sample code: * *


    * try (PublisherClient publisherClient = PublisherClient.create()) {
-   *   String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+   *   String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
    *   List<String> permissions = new ArrayList<>();
    *   TestIamPermissionsRequest request = TestIamPermissionsRequest.newBuilder()
    *     .setResource(formattedResource)
diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/SubscriberClient.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/SubscriberClient.java
index 8bbd1ff67167..7dce0f18408d 100644
--- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/SubscriberClient.java
+++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/SubscriberClient.java
@@ -18,8 +18,8 @@
 import static com.google.cloud.pubsub.spi.v1.PagedResponseWrappers.ListSubscriptionsPagedResponse;
 
 import com.google.api.gax.grpc.ChannelAndExecutor;
+import com.google.api.gax.grpc.StreamingCallable;
 import com.google.api.gax.grpc.UnaryCallable;
-import com.google.api.gax.protobuf.PathTemplate;
 import com.google.iam.v1.GetIamPolicyRequest;
 import com.google.iam.v1.Policy;
 import com.google.iam.v1.SetIamPolicyRequest;
@@ -38,6 +38,8 @@
 import com.google.pubsub.v1.PullRequest;
 import com.google.pubsub.v1.PullResponse;
 import com.google.pubsub.v1.PushConfig;
+import com.google.pubsub.v1.StreamingPullRequest;
+import com.google.pubsub.v1.StreamingPullResponse;
 import com.google.pubsub.v1.Subscription;
 import com.google.pubsub.v1.SubscriptionName;
 import com.google.pubsub.v1.TopicName;
@@ -127,71 +129,14 @@ public class SubscriberClient implements AutoCloseable {
   private final UnaryCallable modifyAckDeadlineCallable;
   private final UnaryCallable acknowledgeCallable;
   private final UnaryCallable pullCallable;
+  private final StreamingCallable
+      streamingPullCallable;
   private final UnaryCallable modifyPushConfigCallable;
   private final UnaryCallable setIamPolicyCallable;
   private final UnaryCallable getIamPolicyCallable;
   private final UnaryCallable
       testIamPermissionsCallable;
 
-  private static final PathTemplate PROJECT_PATH_TEMPLATE =
-      PathTemplate.createWithoutUrlEncoding("projects/{project}");
-
-  private static final PathTemplate SUBSCRIPTION_PATH_TEMPLATE =
-      PathTemplate.createWithoutUrlEncoding("projects/{project}/subscriptions/{subscription}");
-
-  private static final PathTemplate TOPIC_PATH_TEMPLATE =
-      PathTemplate.createWithoutUrlEncoding("projects/{project}/topics/{topic}");
-
-  /** Formats a string containing the fully-qualified path to represent a project resource. */
-  public static final String formatProjectName(String project) {
-    return PROJECT_PATH_TEMPLATE.instantiate("project", project);
-  }
-
-  /** Formats a string containing the fully-qualified path to represent a subscription resource. */
-  public static final String formatSubscriptionName(String project, String subscription) {
-    return SUBSCRIPTION_PATH_TEMPLATE.instantiate(
-        "project", project,
-        "subscription", subscription);
-  }
-
-  /** Formats a string containing the fully-qualified path to represent a topic resource. */
-  public static final String formatTopicName(String project, String topic) {
-    return TOPIC_PATH_TEMPLATE.instantiate(
-        "project", project,
-        "topic", topic);
-  }
-
-  /** Parses the project from the given fully-qualified path which represents a project resource. */
-  public static final String parseProjectFromProjectName(String projectName) {
-    return PROJECT_PATH_TEMPLATE.parse(projectName).get("project");
-  }
-
-  /**
-   * Parses the project from the given fully-qualified path which represents a subscription
-   * resource.
-   */
-  public static final String parseProjectFromSubscriptionName(String subscriptionName) {
-    return SUBSCRIPTION_PATH_TEMPLATE.parse(subscriptionName).get("project");
-  }
-
-  /**
-   * Parses the subscription from the given fully-qualified path which represents a subscription
-   * resource.
-   */
-  public static final String parseSubscriptionFromSubscriptionName(String subscriptionName) {
-    return SUBSCRIPTION_PATH_TEMPLATE.parse(subscriptionName).get("subscription");
-  }
-
-  /** Parses the project from the given fully-qualified path which represents a topic resource. */
-  public static final String parseProjectFromTopicName(String topicName) {
-    return TOPIC_PATH_TEMPLATE.parse(topicName).get("project");
-  }
-
-  /** Parses the topic from the given fully-qualified path which represents a topic resource. */
-  public static final String parseTopicFromTopicName(String topicName) {
-    return TOPIC_PATH_TEMPLATE.parse(topicName).get("topic");
-  }
-
   /** Constructs an instance of SubscriberClient with default settings. */
   public static final SubscriberClient create() throws IOException {
     return create(SubscriberSettings.defaultBuilder().build());
@@ -231,6 +176,8 @@ protected SubscriberClient(SubscriberSettings settings) throws IOException {
     this.acknowledgeCallable =
         UnaryCallable.create(settings.acknowledgeSettings(), this.channel, this.executor);
     this.pullCallable = UnaryCallable.create(settings.pullSettings(), this.channel, this.executor);
+    this.streamingPullCallable =
+        StreamingCallable.create(settings.streamingPullSettings(), this.channel);
     this.modifyPushConfigCallable =
         UnaryCallable.create(settings.modifyPushConfigSettings(), this.channel, this.executor);
     this.setIamPolicyCallable =
@@ -270,8 +217,10 @@ public final SubscriberSettings getSettings() {
    * `ALREADY_EXISTS`. If the corresponding topic doesn't exist, returns `NOT_FOUND`.
    *
    * 

If the name is not provided in the request, the server will assign a random name for this - * subscription on the same project as the topic. Note that for REST API requests, you must - * specify a name. + * subscription on the same project as the topic, conforming to the [resource name + * format](https://cloud.google.com/pubsub/docs/overview#names). The generated name is populated + * in the returned Subscription object. Note that for REST API requests, you must specify a name + * in the request. * *

Sample code: * @@ -290,8 +239,9 @@ public final SubscriberSettings getSettings() { * letter, and contain only letters (`[A-Za-z]`), numbers (`[0-9]`), dashes (`-`), underscores * (`_`), periods (`.`), tildes (`~`), plus (`+`) or percent signs (`%`). It must be between 3 * and 255 characters in length, and it must not start with `"goog"`. - * @param topic The name of the topic from which this subscription is receiving messages. The - * value of this field will be `_deleted-topic_` if the topic has been deleted. + * @param topic The name of the topic from which this subscription is receiving messages. Format + * is `projects/{project}/topics/{topic}`. The value of this field will be `_deleted-topic_` + * if the topic has been deleted. * @param pushConfig If push delivery is used with this subscription, this field is used to * configure it. An empty `pushConfig` signifies that the subscriber will pull and ack * messages using API methods. @@ -301,13 +251,13 @@ public final SubscriberSettings getSettings() { * and will not be delivered again during that time (on a best-effort basis). *

For pull subscriptions, this value is used as the initial value for the ack deadline. To * override this value for a given message, call `ModifyAckDeadline` with the corresponding - * `ack_id` if using pull. The maximum custom deadline you can specify is 600 seconds (10 - * minutes). + * `ack_id` if using pull. The minimum custom deadline you can specify is 10 seconds. The + * maximum custom deadline you can specify is 600 seconds (10 minutes). If this parameter is + * 0, a default value of 10 seconds is used. *

For push delivery, this value is also used to set the request timeout for the call to * the push endpoint. *

If the subscriber never acknowledges the message, the Pub/Sub system will eventually * redeliver the message. - *

If this parameter is 0, a default value of 10 seconds is used. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final Subscription createSubscription( @@ -316,7 +266,7 @@ public final Subscription createSubscription( Subscription request = Subscription.newBuilder() .setNameWithSubscriptionName(name) - .setTopicWithTopicNameOneof(TopicNameOneof.from(topic)) + .setTopicWithTopicNameOneof(topic == null ? null : TopicNameOneof.from(topic)) .setPushConfig(pushConfig) .setAckDeadlineSeconds(ackDeadlineSeconds) .build(); @@ -329,8 +279,10 @@ public final Subscription createSubscription( * `ALREADY_EXISTS`. If the corresponding topic doesn't exist, returns `NOT_FOUND`. * *

If the name is not provided in the request, the server will assign a random name for this - * subscription on the same project as the topic. Note that for REST API requests, you must - * specify a name. + * subscription on the same project as the topic, conforming to the [resource name + * format](https://cloud.google.com/pubsub/docs/overview#names). The generated name is populated + * in the returned Subscription object. Note that for REST API requests, you must specify a name + * in the request. * *

Sample code: * @@ -359,8 +311,10 @@ public final Subscription createSubscription(Subscription request) { * `ALREADY_EXISTS`. If the corresponding topic doesn't exist, returns `NOT_FOUND`. * *

If the name is not provided in the request, the server will assign a random name for this - * subscription on the same project as the topic. Note that for REST API requests, you must - * specify a name. + * subscription on the same project as the topic, conforming to the [resource name + * format](https://cloud.google.com/pubsub/docs/overview#names). The generated name is populated + * in the returned Subscription object. Note that for REST API requests, you must specify a name + * in the request. * *

Sample code: * @@ -395,7 +349,8 @@ public final UnaryCallable createSubscriptionCallabl * } *

* - * @param subscription The name of the subscription to get. + * @param subscription The name of the subscription to get. Format is + * `projects/{project}/subscriptions/{sub}`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final Subscription getSubscription(SubscriptionName subscription) { @@ -467,7 +422,8 @@ public final UnaryCallable getSubscription * } *
* - * @param project The name of the cloud project that subscriptions belong to. + * @param project The name of the cloud project that subscriptions belong to. Format is + * `projects/{project}`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final ListSubscriptionsPagedResponse listSubscriptions(ProjectName project) { @@ -560,10 +516,10 @@ public final ListSubscriptionsPagedResponse listSubscriptions(ListSubscriptionsR // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Deletes an existing subscription. All pending messages in the subscription are immediately + * Deletes an existing subscription. All messages retained in the subscription are immediately * dropped. Calls to `Pull` after deletion will return `NOT_FOUND`. After a subscription is * deleted, a new one may be created with the same name, but the new one has no association with - * the old subscription, or its topic unless the same topic is specified. + * the old subscription or its topic unless the same topic is specified. * *

Sample code: * @@ -574,7 +530,8 @@ public final ListSubscriptionsPagedResponse listSubscriptions(ListSubscriptionsR * } *

* - * @param subscription The subscription to delete. + * @param subscription The subscription to delete. Format is + * `projects/{project}/subscriptions/{sub}`. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final void deleteSubscription(SubscriptionName subscription) { @@ -588,10 +545,10 @@ public final void deleteSubscription(SubscriptionName subscription) { // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Deletes an existing subscription. All pending messages in the subscription are immediately + * Deletes an existing subscription. All messages retained in the subscription are immediately * dropped. Calls to `Pull` after deletion will return `NOT_FOUND`. After a subscription is * deleted, a new one may be created with the same name, but the new one has no association with - * the old subscription, or its topic unless the same topic is specified. + * the old subscription or its topic unless the same topic is specified. * *

Sample code: * @@ -614,10 +571,10 @@ private final void deleteSubscription(DeleteSubscriptionRequest request) { // AUTO-GENERATED DOCUMENTATION AND METHOD /** - * Deletes an existing subscription. All pending messages in the subscription are immediately + * Deletes an existing subscription. All messages retained in the subscription are immediately * dropped. Calls to `Pull` after deletion will return `NOT_FOUND`. After a subscription is * deleted, a new one may be created with the same name, but the new one has no association with - * the old subscription, or its topic unless the same topic is specified. + * the old subscription or its topic unless the same topic is specified. * *

Sample code: * @@ -655,12 +612,14 @@ public final UnaryCallable deleteSubscriptionC * } *

* - * @param subscription The name of the subscription. + * @param subscription The name of the subscription. Format is + * `projects/{project}/subscriptions/{sub}`. * @param ackIds List of acknowledgment IDs. * @param ackDeadlineSeconds The new ack deadline with respect to the time this request was sent - * to the Pub/Sub system. Must be >= 0. For example, if the value is 10, the new ack - * deadline will expire 10 seconds after the `ModifyAckDeadline` call was made. Specifying - * zero may immediately make the message available for another pull request. + * to the Pub/Sub system. For example, if the value is 10, the new ack deadline will expire 10 + * seconds after the `ModifyAckDeadline` call was made. Specifying zero may immediately make + * the message available for another pull request. The minimum deadline you can specify is 0 + * seconds. The maximum deadline you can specify is 600 seconds (10 minutes). * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final void modifyAckDeadline( @@ -752,7 +711,8 @@ public final UnaryCallable modifyAckDeadlineCal * } *
* - * @param subscription The subscription whose message is being acknowledged. + * @param subscription The subscription whose message is being acknowledged. Format is + * `projects/{project}/subscriptions/{sub}`. * @param ackIds The acknowledgment ID for the messages being acknowledged that was returned by * the Pub/Sub system in the `Pull` response. Must not be empty. * @throws com.google.api.gax.grpc.ApiException if the remote call fails @@ -841,11 +801,13 @@ public final UnaryCallable acknowledgeCallable() { * } *
* - * @param subscription The subscription from which messages should be pulled. - * @param returnImmediately If this is specified as true the system will respond immediately even - * if it is not able to return a message in the `Pull` response. Otherwise the system is - * allowed to wait until at least one message is available rather than returning no messages. - * The client may cancel the request if it does not wish to wait any longer for the response. + * @param subscription The subscription from which messages should be pulled. Format is + * `projects/{project}/subscriptions/{sub}`. + * @param returnImmediately If this field set to true, the system will respond immediately even if + * it there are no messages available to return in the `Pull` response. Otherwise, the system + * may wait (for a bounded amount of time) until at least one message is available, rather + * than returning no messages. The client may cancel the request if it does not wish to wait + * any longer for the response. * @param maxMessages The maximum number of messages returned for this request. The Pub/Sub system * may return fewer than the number specified. * @throws com.google.api.gax.grpc.ApiException if the remote call fails @@ -915,6 +877,59 @@ public final UnaryCallable pullCallable() { return pullCallable; } + // AUTO-GENERATED DOCUMENTATION AND METHOD + /** + * (EXPERIMENTAL) StreamingPull is an experimental feature. This RPC will respond with + * UNIMPLEMENTED errors unless you have been invited to test this feature. Contact + * cloud-pubsub{@literal @}google.com with any questions. + * + *

Establishes a stream with the server, which sends messages down to the client. The client + * streams acknowledgements and ack deadline modifications back to the server. The server will + * close the stream and return the status on any error. The server may close the stream with + * status `OK` to reassign server-side resources, in which case, the client should re-establish + * the stream. `UNAVAILABLE` may also be returned in the case of a transient error (e.g., a server + * restart). These should also be retried by the client. Flow control can be achieved by + * configuring the underlying RPC channel. + * + *

Sample code: + * + *


+   * try (SubscriberClient subscriberClient = SubscriberClient.create()) {
+   *   StreamObserver<StreamingPullResponse> responseObserver =
+   *       new StreamObserver<StreamingPullResponse>() {
+   *         {@literal @}Override
+   *         public void onNext(StreamingPullResponse response) {
+   *           // Do something when receive a response
+   *         }
+   *
+   *         {@literal @}Override
+   *         public void onError(Throwable t) {
+   *           // Add error-handling
+   *         }
+   *
+   *         {@literal @}Override
+   *         public void onCompleted() {
+   *           // Do something when complete.
+   *         }
+   *       };
+   *   StreamObserver<StreamingRecognizeRequest> requestObserver =
+   *       subscriberClient.streamingPullCallable().bidiStreamingCall(responseObserver)});
+   *
+   *   SubscriptionName subscription = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]");
+   *   int streamAckDeadlineSeconds = 0;
+   *   StreamingPullRequest request = StreamingPullRequest.newBuilder()
+   *     .setSubscriptionWithSubscriptionName(subscription)
+   *     .setStreamAckDeadlineSeconds(streamAckDeadlineSeconds)
+   *     .build();
+   *   requestObserver.onNext(request);
+   * }
+   * 
+ */ + public final StreamingCallable + streamingPullCallable() { + return streamingPullCallable; + } + // AUTO-GENERATED DOCUMENTATION AND METHOD /** * Modifies the `PushConfig` for a specified subscription. @@ -934,7 +949,8 @@ public final UnaryCallable pullCallable() { * } *
* - * @param subscription The name of the subscription. + * @param subscription The name of the subscription. Format is + * `projects/{project}/subscriptions/{sub}`. * @param pushConfig The push configuration for future deliveries. *

An empty `pushConfig` indicates that the Pub/Sub system should stop pushing messages * from the given subscription and allow messages to be pulled and acknowledged - effectively @@ -1018,7 +1034,7 @@ public final UnaryCallable modifyPushConfigCalla * *


    * try (SubscriberClient subscriberClient = SubscriberClient.create()) {
-   *   String formattedResource = SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+   *   String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
    *   Policy policy = Policy.newBuilder().build();
    *   Policy response = subscriberClient.setIamPolicy(formattedResource, policy);
    * }
@@ -1033,7 +1049,7 @@ public final UnaryCallable modifyPushConfigCalla
    * @throws com.google.api.gax.grpc.ApiException if the remote call fails
    */
   public final Policy setIamPolicy(String resource, Policy policy) {
-    SUBSCRIPTION_PATH_TEMPLATE.validate(resource, "setIamPolicy");
+
     SetIamPolicyRequest request =
         SetIamPolicyRequest.newBuilder().setResource(resource).setPolicy(policy).build();
     return setIamPolicy(request);
@@ -1047,7 +1063,7 @@ public final Policy setIamPolicy(String resource, Policy policy) {
    *
    * 

    * try (SubscriberClient subscriberClient = SubscriberClient.create()) {
-   *   String formattedResource = SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+   *   String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
    *   Policy policy = Policy.newBuilder().build();
    *   SetIamPolicyRequest request = SetIamPolicyRequest.newBuilder()
    *     .setResource(formattedResource)
@@ -1072,7 +1088,7 @@ public final Policy setIamPolicy(SetIamPolicyRequest request) {
    *
    * 

    * try (SubscriberClient subscriberClient = SubscriberClient.create()) {
-   *   String formattedResource = SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+   *   String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
    *   Policy policy = Policy.newBuilder().build();
    *   SetIamPolicyRequest request = SetIamPolicyRequest.newBuilder()
    *     .setResource(formattedResource)
@@ -1097,7 +1113,7 @@ public final UnaryCallable setIamPolicyCallable() {
    *
    * 

    * try (SubscriberClient subscriberClient = SubscriberClient.create()) {
-   *   String formattedResource = SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+   *   String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
    *   Policy response = subscriberClient.getIamPolicy(formattedResource);
    * }
    * 
@@ -1108,7 +1124,7 @@ public final UnaryCallable setIamPolicyCallable() { * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ public final Policy getIamPolicy(String resource) { - SUBSCRIPTION_PATH_TEMPLATE.validate(resource, "getIamPolicy"); + GetIamPolicyRequest request = GetIamPolicyRequest.newBuilder().setResource(resource).build(); return getIamPolicy(request); } @@ -1122,7 +1138,7 @@ public final Policy getIamPolicy(String resource) { * *

    * try (SubscriberClient subscriberClient = SubscriberClient.create()) {
-   *   String formattedResource = SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+   *   String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
    *   GetIamPolicyRequest request = GetIamPolicyRequest.newBuilder()
    *     .setResource(formattedResource)
    *     .build();
@@ -1146,7 +1162,7 @@ private final Policy getIamPolicy(GetIamPolicyRequest request) {
    *
    * 

    * try (SubscriberClient subscriberClient = SubscriberClient.create()) {
-   *   String formattedResource = SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+   *   String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
    *   GetIamPolicyRequest request = GetIamPolicyRequest.newBuilder()
    *     .setResource(formattedResource)
    *     .build();
@@ -1162,13 +1178,14 @@ public final UnaryCallable getIamPolicyCallable() {
 
   // AUTO-GENERATED DOCUMENTATION AND METHOD
   /**
-   * Returns permissions that a caller has on the specified resource.
+   * Returns permissions that a caller has on the specified resource. If the resource does not
+   * exist, this will return an empty set of permissions, not a NOT_FOUND error.
    *
    * 

Sample code: * *


    * try (SubscriberClient subscriberClient = SubscriberClient.create()) {
-   *   String formattedResource = SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+   *   String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
    *   List<String> permissions = new ArrayList<>();
    *   TestIamPermissionsResponse response = subscriberClient.testIamPermissions(formattedResource, permissions);
    * }
@@ -1184,7 +1201,7 @@ public final UnaryCallable getIamPolicyCallable() {
    */
   public final TestIamPermissionsResponse testIamPermissions(
       String resource, List permissions) {
-    SUBSCRIPTION_PATH_TEMPLATE.validate(resource, "testIamPermissions");
+
     TestIamPermissionsRequest request =
         TestIamPermissionsRequest.newBuilder()
             .setResource(resource)
@@ -1195,13 +1212,14 @@ public final TestIamPermissionsResponse testIamPermissions(
 
   // AUTO-GENERATED DOCUMENTATION AND METHOD
   /**
-   * Returns permissions that a caller has on the specified resource.
+   * Returns permissions that a caller has on the specified resource. If the resource does not
+   * exist, this will return an empty set of permissions, not a NOT_FOUND error.
    *
    * 

Sample code: * *


    * try (SubscriberClient subscriberClient = SubscriberClient.create()) {
-   *   String formattedResource = SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+   *   String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
    *   List<String> permissions = new ArrayList<>();
    *   TestIamPermissionsRequest request = TestIamPermissionsRequest.newBuilder()
    *     .setResource(formattedResource)
@@ -1220,13 +1238,14 @@ public final TestIamPermissionsResponse testIamPermissions(TestIamPermissionsReq
 
   // AUTO-GENERATED DOCUMENTATION AND METHOD
   /**
-   * Returns permissions that a caller has on the specified resource.
+   * Returns permissions that a caller has on the specified resource. If the resource does not
+   * exist, this will return an empty set of permissions, not a NOT_FOUND error.
    *
    * 

Sample code: * *


    * try (SubscriberClient subscriberClient = SubscriberClient.create()) {
-   *   String formattedResource = SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+   *   String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
    *   List<String> permissions = new ArrayList<>();
    *   TestIamPermissionsRequest request = TestIamPermissionsRequest.newBuilder()
    *     .setResource(formattedResource)
diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/SubscriberSettings.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/SubscriberSettings.java
index 971e00f2df72..1366d8e10e90 100644
--- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/SubscriberSettings.java
+++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/SubscriberSettings.java
@@ -29,6 +29,7 @@
 import com.google.api.gax.grpc.PagedListDescriptor;
 import com.google.api.gax.grpc.PagedListResponseFactory;
 import com.google.api.gax.grpc.SimpleCallSettings;
+import com.google.api.gax.grpc.StreamingCallSettings;
 import com.google.api.gax.grpc.UnaryCallSettings;
 import com.google.api.gax.grpc.UnaryCallable;
 import com.google.common.collect.ImmutableList;
@@ -53,6 +54,8 @@
 import com.google.pubsub.v1.ModifyPushConfigRequest;
 import com.google.pubsub.v1.PullRequest;
 import com.google.pubsub.v1.PullResponse;
+import com.google.pubsub.v1.StreamingPullRequest;
+import com.google.pubsub.v1.StreamingPullResponse;
 import com.google.pubsub.v1.SubscriberGrpc;
 import com.google.pubsub.v1.Subscription;
 import io.grpc.Status;
@@ -111,6 +114,8 @@ public class SubscriberSettings extends ClientSettings {
   private final SimpleCallSettings modifyAckDeadlineSettings;
   private final SimpleCallSettings acknowledgeSettings;
   private final SimpleCallSettings pullSettings;
+  private final StreamingCallSettings
+      streamingPullSettings;
   private final SimpleCallSettings modifyPushConfigSettings;
   private final SimpleCallSettings setIamPolicySettings;
   private final SimpleCallSettings getIamPolicySettings;
@@ -154,6 +159,12 @@ public SimpleCallSettings pullSettings() {
     return pullSettings;
   }
 
+  /** Returns the object with the settings used for calls to streamingPull. */
+  public StreamingCallSettings
+      streamingPullSettings() {
+    return streamingPullSettings;
+  }
+
   /** Returns the object with the settings used for calls to modifyPushConfig. */
   public SimpleCallSettings modifyPushConfigSettings() {
     return modifyPushConfigSettings;
@@ -233,6 +244,7 @@ private SubscriberSettings(Builder settingsBuilder) throws IOException {
     modifyAckDeadlineSettings = settingsBuilder.modifyAckDeadlineSettings().build();
     acknowledgeSettings = settingsBuilder.acknowledgeSettings().build();
     pullSettings = settingsBuilder.pullSettings().build();
+    streamingPullSettings = settingsBuilder.streamingPullSettings().build();
     modifyPushConfigSettings = settingsBuilder.modifyPushConfigSettings().build();
     setIamPolicySettings = settingsBuilder.setIamPolicySettings().build();
     getIamPolicySettings = settingsBuilder.getIamPolicySettings().build();
@@ -311,6 +323,8 @@ public static class Builder extends ClientSettings.Builder {
         modifyAckDeadlineSettings;
     private final SimpleCallSettings.Builder acknowledgeSettings;
     private final SimpleCallSettings.Builder pullSettings;
+    private final StreamingCallSettings.Builder
+        streamingPullSettings;
     private final SimpleCallSettings.Builder
         modifyPushConfigSettings;
     private final SimpleCallSettings.Builder setIamPolicySettings;
@@ -382,6 +396,9 @@ private Builder() {
 
       pullSettings = SimpleCallSettings.newBuilder(SubscriberGrpc.METHOD_PULL);
 
+      streamingPullSettings =
+          StreamingCallSettings.newBuilder(SubscriberGrpc.METHOD_STREAMING_PULL);
+
       modifyPushConfigSettings =
           SimpleCallSettings.newBuilder(SubscriberGrpc.METHOD_MODIFY_PUSH_CONFIG);
 
@@ -478,6 +495,7 @@ private Builder(SubscriberSettings settings) {
       modifyAckDeadlineSettings = settings.modifyAckDeadlineSettings.toBuilder();
       acknowledgeSettings = settings.acknowledgeSettings.toBuilder();
       pullSettings = settings.pullSettings.toBuilder();
+      streamingPullSettings = settings.streamingPullSettings.toBuilder();
       modifyPushConfigSettings = settings.modifyPushConfigSettings.toBuilder();
       setIamPolicySettings = settings.setIamPolicySettings.toBuilder();
       getIamPolicySettings = settings.getIamPolicySettings.toBuilder();
@@ -561,6 +579,12 @@ public SimpleCallSettings.Builder pullSettings() {
       return pullSettings;
     }
 
+    /** Returns the builder for the settings used for calls to streamingPull. */
+    public StreamingCallSettings.Builder
+        streamingPullSettings() {
+      return streamingPullSettings;
+    }
+
     /** Returns the builder for the settings used for calls to modifyPushConfig. */
     public SimpleCallSettings.Builder modifyPushConfigSettings() {
       return modifyPushConfigSettings;
diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/PubSubImplTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/PubSubImplTest.java
index 9ace15da5027..4287fab025fe 100644
--- a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/PubSubImplTest.java
+++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/PubSubImplTest.java
@@ -16,7 +16,6 @@
 
 package com.google.cloud.pubsub;
 
-import static com.google.cloud.pubsub.spi.v1.SubscriberClient.formatSubscriptionName;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -68,16 +67,7 @@
 import com.google.pubsub.v1.PublishResponse;
 import com.google.pubsub.v1.PullRequest;
 import com.google.pubsub.v1.PullResponse;
-
-import org.easymock.Capture;
-import org.easymock.EasyMock;
-import org.easymock.IAnswer;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
+import com.google.pubsub.v1.SubscriptionName;
 import java.io.IOException;
 import java.util.Iterator;
 import java.util.List;
@@ -86,6 +76,14 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 public class PubSubImplTest {
 
@@ -143,8 +141,8 @@ public com.google.pubsub.v1.Subscription apply(SubscriptionInfo subscriptionInfo
       new Function() {
         @Override
         public String apply(SubscriptionId subscriptionId) {
-          return formatSubscriptionName(subscriptionId.getProject(),
-              subscriptionId.getSubscription());
+          return SubscriptionName.create(subscriptionId.getProject(),
+              subscriptionId.getSubscription()).toString();
         }
       };
   private static final MessageProcessor DO_NOTHING = new MessageProcessor() {
diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/MockSubscriberImpl.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/MockSubscriberImpl.java
index 2bfcd52631d0..cb589e8e0028 100644
--- a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/MockSubscriberImpl.java
+++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/MockSubscriberImpl.java
@@ -26,6 +26,8 @@
 import com.google.pubsub.v1.ModifyPushConfigRequest;
 import com.google.pubsub.v1.PullRequest;
 import com.google.pubsub.v1.PullResponse;
+import com.google.pubsub.v1.StreamingPullRequest;
+import com.google.pubsub.v1.StreamingPullResponse;
 import com.google.pubsub.v1.SubscriberGrpc.SubscriberImplBase;
 import com.google.pubsub.v1.Subscription;
 import io.grpc.stub.StreamObserver;
@@ -169,6 +171,36 @@ public void pull(PullRequest request, StreamObserver responseObser
     }
   }
 
+  @Override
+  public StreamObserver streamingPull(
+      final StreamObserver responseObserver) {
+    final Object response = responses.remove();
+    StreamObserver requestObserver =
+        new StreamObserver() {
+          @Override
+          public void onNext(StreamingPullRequest value) {
+            if (response instanceof StreamingPullResponse) {
+              responseObserver.onNext((StreamingPullResponse) response);
+            } else if (response instanceof Exception) {
+              responseObserver.onError((Exception) response);
+            } else {
+              responseObserver.onError(new IllegalArgumentException("Unrecognized response type"));
+            }
+          }
+
+          @Override
+          public void onError(Throwable t) {
+            responseObserver.onError(t);
+          }
+
+          @Override
+          public void onCompleted() {
+            responseObserver.onCompleted();
+          }
+        };
+    return requestObserver;
+  }
+
   @Override
   public void modifyPushConfig(
       ModifyPushConfigRequest request, StreamObserver responseObserver) {
diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/PublisherTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/PublisherTest.java
index 45066d67c31b..dda2496c08ab 100644
--- a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/PublisherTest.java
+++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/PublisherTest.java
@@ -345,7 +345,7 @@ public void setIamPolicyTest() {
     Policy expectedResponse = Policy.newBuilder().setVersion(version).setEtag(etag).build();
     mockIAMPolicy.addResponse(expectedResponse);
 
-    String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+    String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
     Policy policy = Policy.newBuilder().build();
 
     Policy actualResponse = client.setIamPolicy(formattedResource, policy);
@@ -366,7 +366,7 @@ public void setIamPolicyExceptionTest() throws Exception {
     mockIAMPolicy.addException(exception);
 
     try {
-      String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+      String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
       Policy policy = Policy.newBuilder().build();
 
       client.setIamPolicy(formattedResource, policy);
@@ -384,7 +384,7 @@ public void getIamPolicyTest() {
     Policy expectedResponse = Policy.newBuilder().setVersion(version).setEtag(etag).build();
     mockIAMPolicy.addResponse(expectedResponse);
 
-    String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+    String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
 
     Policy actualResponse = client.getIamPolicy(formattedResource);
     Assert.assertEquals(expectedResponse, actualResponse);
@@ -403,7 +403,7 @@ public void getIamPolicyExceptionTest() throws Exception {
     mockIAMPolicy.addException(exception);
 
     try {
-      String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+      String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
 
       client.getIamPolicy(formattedResource);
       Assert.fail("No exception raised");
@@ -418,7 +418,7 @@ public void testIamPermissionsTest() {
     TestIamPermissionsResponse expectedResponse = TestIamPermissionsResponse.newBuilder().build();
     mockIAMPolicy.addResponse(expectedResponse);
 
-    String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+    String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
     List permissions = new ArrayList<>();
 
     TestIamPermissionsResponse actualResponse =
@@ -440,7 +440,7 @@ public void testIamPermissionsExceptionTest() throws Exception {
     mockIAMPolicy.addException(exception);
 
     try {
-      String formattedResource = PublisherClient.formatTopicName("[PROJECT]", "[TOPIC]");
+      String formattedResource = TopicName.create("[PROJECT]", "[TOPIC]").toString();
       List permissions = new ArrayList<>();
 
       client.testIamPermissions(formattedResource, permissions);
diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/SubscriberTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/SubscriberTest.java
index 7da01dd91701..9b7597bfcff7 100644
--- a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/SubscriberTest.java
+++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/SubscriberTest.java
@@ -18,8 +18,10 @@
 import static com.google.cloud.pubsub.spi.v1.PagedResponseWrappers.ListSubscriptionsPagedResponse;
 
 import com.google.api.gax.grpc.ApiException;
+import com.google.api.gax.grpc.StreamingCallable;
 import com.google.api.gax.testing.MockGrpcService;
 import com.google.api.gax.testing.MockServiceHelper;
+import com.google.api.gax.testing.MockStreamObserver;
 import com.google.common.collect.Lists;
 import com.google.iam.v1.GetIamPolicyRequest;
 import com.google.iam.v1.Policy;
@@ -40,16 +42,20 @@
 import com.google.pubsub.v1.PullRequest;
 import com.google.pubsub.v1.PullResponse;
 import com.google.pubsub.v1.PushConfig;
+import com.google.pubsub.v1.StreamingPullRequest;
+import com.google.pubsub.v1.StreamingPullResponse;
 import com.google.pubsub.v1.Subscription;
 import com.google.pubsub.v1.SubscriptionName;
 import com.google.pubsub.v1.TopicName;
 import com.google.pubsub.v1.TopicNameOneof;
 import io.grpc.Status;
 import io.grpc.StatusRuntimeException;
+import io.grpc.stub.StreamObserver;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.ExecutionException;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -383,6 +389,66 @@ public void pullExceptionTest() throws Exception {
     }
   }
 
+  @Test
+  @SuppressWarnings("all")
+  public void streamingPullTest() throws Exception {
+    StreamingPullResponse expectedResponse = StreamingPullResponse.newBuilder().build();
+    mockSubscriber.addResponse(expectedResponse);
+    SubscriptionName subscription = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]");
+    int streamAckDeadlineSeconds = 1875467245;
+    StreamingPullRequest request =
+        StreamingPullRequest.newBuilder()
+            .setSubscriptionWithSubscriptionName(subscription)
+            .setStreamAckDeadlineSeconds(streamAckDeadlineSeconds)
+            .build();
+
+    MockStreamObserver responseObserver = new MockStreamObserver<>();
+
+    StreamingCallable callable =
+        client.streamingPullCallable();
+    StreamObserver requestObserver =
+        callable.bidiStreamingCall(responseObserver);
+
+    requestObserver.onNext(request);
+    requestObserver.onCompleted();
+
+    List actualResponses = responseObserver.future().get();
+    Assert.assertEquals(1, actualResponses.size());
+    Assert.assertEquals(expectedResponse, actualResponses.get(0));
+  }
+
+  @Test
+  @SuppressWarnings("all")
+  public void streamingPullExceptionTest() throws Exception {
+    StatusRuntimeException exception = new StatusRuntimeException(Status.INTERNAL);
+    mockSubscriber.addException(exception);
+    SubscriptionName subscription = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]");
+    int streamAckDeadlineSeconds = 1875467245;
+    StreamingPullRequest request =
+        StreamingPullRequest.newBuilder()
+            .setSubscriptionWithSubscriptionName(subscription)
+            .setStreamAckDeadlineSeconds(streamAckDeadlineSeconds)
+            .build();
+
+    MockStreamObserver responseObserver = new MockStreamObserver<>();
+
+    StreamingCallable callable =
+        client.streamingPullCallable();
+    StreamObserver requestObserver =
+        callable.bidiStreamingCall(responseObserver);
+
+    requestObserver.onNext(request);
+
+    try {
+      List actualResponses = responseObserver.future().get();
+      Assert.fail("No exception thrown");
+    } catch (ExecutionException e) {
+      Assert.assertTrue(e.getCause() instanceof StatusRuntimeException);
+      StatusRuntimeException statusException = (StatusRuntimeException) e.getCause();
+      Assert.assertEquals(Status.INTERNAL, statusException.getStatus());
+    }
+  }
+
   @Test
   @SuppressWarnings("all")
   public void modifyPushConfigTest() {
@@ -427,8 +493,7 @@ public void setIamPolicyTest() {
     Policy expectedResponse = Policy.newBuilder().setVersion(version).setEtag(etag).build();
     mockIAMPolicy.addResponse(expectedResponse);
 
-    String formattedResource =
-        SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+    String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
     Policy policy = Policy.newBuilder().build();
 
     Policy actualResponse = client.setIamPolicy(formattedResource, policy);
@@ -449,8 +514,7 @@ public void setIamPolicyExceptionTest() throws Exception {
     mockIAMPolicy.addException(exception);
 
     try {
-      String formattedResource =
-          SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+      String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
       Policy policy = Policy.newBuilder().build();
 
       client.setIamPolicy(formattedResource, policy);
@@ -468,8 +532,7 @@ public void getIamPolicyTest() {
     Policy expectedResponse = Policy.newBuilder().setVersion(version).setEtag(etag).build();
     mockIAMPolicy.addResponse(expectedResponse);
 
-    String formattedResource =
-        SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+    String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
 
     Policy actualResponse = client.getIamPolicy(formattedResource);
     Assert.assertEquals(expectedResponse, actualResponse);
@@ -488,8 +551,7 @@ public void getIamPolicyExceptionTest() throws Exception {
     mockIAMPolicy.addException(exception);
 
     try {
-      String formattedResource =
-          SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+      String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
 
       client.getIamPolicy(formattedResource);
       Assert.fail("No exception raised");
@@ -504,8 +566,7 @@ public void testIamPermissionsTest() {
     TestIamPermissionsResponse expectedResponse = TestIamPermissionsResponse.newBuilder().build();
     mockIAMPolicy.addResponse(expectedResponse);
 
-    String formattedResource =
-        SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+    String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
     List permissions = new ArrayList<>();
 
     TestIamPermissionsResponse actualResponse =
@@ -527,8 +588,7 @@ public void testIamPermissionsExceptionTest() throws Exception {
     mockIAMPolicy.addException(exception);
 
     try {
-      String formattedResource =
-          SubscriberClient.formatSubscriptionName("[PROJECT]", "[SUBSCRIPTION]");
+      String formattedResource = SubscriptionName.create("[PROJECT]", "[SUBSCRIPTION]").toString();
       List permissions = new ArrayList<>();
 
       client.testIamPermissions(formattedResource, permissions);
diff --git a/google-cloud-speech/pom.xml b/google-cloud-speech/pom.xml
index 52fd58a97b58..3e48afaad48e 100644
--- a/google-cloud-speech/pom.xml
+++ b/google-cloud-speech/pom.xml
@@ -30,7 +30,7 @@
     
       com.google.api.grpc
       grpc-google-cloud-speech-v1beta1
-      0.1.3
+      0.1.5
       
         
           io.grpc
diff --git a/google-cloud-trace/pom.xml b/google-cloud-trace/pom.xml
index f787b6c3924b..7a0869402104 100644
--- a/google-cloud-trace/pom.xml
+++ b/google-cloud-trace/pom.xml
@@ -30,7 +30,7 @@
     
       com.google.api.grpc
       grpc-google-cloud-trace-v1
-      0.1.3
+      0.1.5
       
         
           io.grpc
diff --git a/google-cloud-vision/pom.xml b/google-cloud-vision/pom.xml
index f14e51c7b2e8..3b674429969e 100644
--- a/google-cloud-vision/pom.xml
+++ b/google-cloud-vision/pom.xml
@@ -30,7 +30,7 @@
     
       com.google.api.grpc
       grpc-google-cloud-vision-v1
-      0.1.3
+      0.1.5
       
         
           io.grpc

From cd451704449524da6e9ee177812d2f6e669d028c Mon Sep 17 00:00:00 2001
From: garrettjonesgoogle 
Date: Thu, 5 Jan 2017 16:19:27 -0800
Subject: [PATCH 05/22] Updating grpc dependency to 1.0.3 (#1504)

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 8534ae252605..1eeb70f31789 100644
--- a/pom.xml
+++ b/pom.xml
@@ -91,7 +91,7 @@
     UTF-8
     github
     0.6.0
-    1.0.1
+    1.0.3
     0.8.1-SNAPSHOT
     0.8.1-beta-SNAPSHOT
     ${beta.version}

From 3da8747dad06696dc298f25b96682bac6ba0e44f Mon Sep 17 00:00:00 2001
From: garrettjonesgoogle 
Date: Mon, 9 Jan 2017 09:08:26 -0800
Subject: [PATCH 06/22] Release 0.8.1 (#1512)

---
 google-cloud-bigquery/pom.xml                          | 2 +-
 google-cloud-compute/pom.xml                           | 2 +-
 google-cloud-contrib/google-cloud-nio-examples/pom.xml | 2 +-
 google-cloud-contrib/google-cloud-nio/pom.xml          | 2 +-
 google-cloud-contrib/pom.xml                           | 2 +-
 google-cloud-core/pom.xml                              | 2 +-
 google-cloud-datastore/pom.xml                         | 2 +-
 google-cloud-dns/pom.xml                               | 2 +-
 google-cloud-errorreporting/pom.xml                    | 2 +-
 google-cloud-examples/pom.xml                          | 2 +-
 google-cloud-language/pom.xml                          | 2 +-
 google-cloud-logging/pom.xml                           | 2 +-
 google-cloud-monitoring/pom.xml                        | 2 +-
 google-cloud-pubsub/pom.xml                            | 2 +-
 google-cloud-resourcemanager/pom.xml                   | 2 +-
 google-cloud-speech/pom.xml                            | 2 +-
 google-cloud-storage/pom.xml                           | 2 +-
 google-cloud-trace/pom.xml                             | 2 +-
 google-cloud-translate/pom.xml                         | 2 +-
 google-cloud-vision/pom.xml                            | 2 +-
 google-cloud/pom.xml                                   | 2 +-
 pom.xml                                                | 6 +++---
 22 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/google-cloud-bigquery/pom.xml b/google-cloud-bigquery/pom.xml
index 73383afce15f..409ad6faf989 100644
--- a/google-cloud-bigquery/pom.xml
+++ b/google-cloud-bigquery/pom.xml
@@ -12,7 +12,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-bigquery
diff --git a/google-cloud-compute/pom.xml b/google-cloud-compute/pom.xml
index a9203ad718ac..b1851df41b92 100644
--- a/google-cloud-compute/pom.xml
+++ b/google-cloud-compute/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-compute
diff --git a/google-cloud-contrib/google-cloud-nio-examples/pom.xml b/google-cloud-contrib/google-cloud-nio-examples/pom.xml
index 2b07df873af8..f5c7fef93cbc 100644
--- a/google-cloud-contrib/google-cloud-nio-examples/pom.xml
+++ b/google-cloud-contrib/google-cloud-nio-examples/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-contrib
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-nio-examples
diff --git a/google-cloud-contrib/google-cloud-nio/pom.xml b/google-cloud-contrib/google-cloud-nio/pom.xml
index 0646be3fe3da..8b475667adcd 100644
--- a/google-cloud-contrib/google-cloud-nio/pom.xml
+++ b/google-cloud-contrib/google-cloud-nio/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-contrib
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-nio
diff --git a/google-cloud-contrib/pom.xml b/google-cloud-contrib/pom.xml
index 81f42e03b96d..3a1b299879ef 100644
--- a/google-cloud-contrib/pom.xml
+++ b/google-cloud-contrib/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-contrib
diff --git a/google-cloud-core/pom.xml b/google-cloud-core/pom.xml
index 613c7f1e98d5..c0bfd8d15eb8 100644
--- a/google-cloud-core/pom.xml
+++ b/google-cloud-core/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-core
diff --git a/google-cloud-datastore/pom.xml b/google-cloud-datastore/pom.xml
index 100ad5edcfd9..dece795ce8a0 100644
--- a/google-cloud-datastore/pom.xml
+++ b/google-cloud-datastore/pom.xml
@@ -12,7 +12,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-datastore
diff --git a/google-cloud-dns/pom.xml b/google-cloud-dns/pom.xml
index a53283c9f453..207b4f8e57ff 100644
--- a/google-cloud-dns/pom.xml
+++ b/google-cloud-dns/pom.xml
@@ -13,7 +13,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-dns
diff --git a/google-cloud-errorreporting/pom.xml b/google-cloud-errorreporting/pom.xml
index 49cda878a49e..480ec1beb953 100644
--- a/google-cloud-errorreporting/pom.xml
+++ b/google-cloud-errorreporting/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-errorreporting
diff --git a/google-cloud-examples/pom.xml b/google-cloud-examples/pom.xml
index ffbb5f46df5f..72f4f4bb975b 100644
--- a/google-cloud-examples/pom.xml
+++ b/google-cloud-examples/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-examples
diff --git a/google-cloud-language/pom.xml b/google-cloud-language/pom.xml
index db3b8fb82678..f14929eaf9cf 100644
--- a/google-cloud-language/pom.xml
+++ b/google-cloud-language/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-language
diff --git a/google-cloud-logging/pom.xml b/google-cloud-logging/pom.xml
index 5ea3038055b1..81f381e1fad9 100644
--- a/google-cloud-logging/pom.xml
+++ b/google-cloud-logging/pom.xml
@@ -12,7 +12,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-logging
diff --git a/google-cloud-monitoring/pom.xml b/google-cloud-monitoring/pom.xml
index ff4c86b3f52d..9ffaf9607666 100644
--- a/google-cloud-monitoring/pom.xml
+++ b/google-cloud-monitoring/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-monitoring
diff --git a/google-cloud-pubsub/pom.xml b/google-cloud-pubsub/pom.xml
index e780732e4b2c..7e62030864c4 100644
--- a/google-cloud-pubsub/pom.xml
+++ b/google-cloud-pubsub/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-pubsub
diff --git a/google-cloud-resourcemanager/pom.xml b/google-cloud-resourcemanager/pom.xml
index cc0da8a2cb30..17b7c544d58f 100644
--- a/google-cloud-resourcemanager/pom.xml
+++ b/google-cloud-resourcemanager/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-resourcemanager
diff --git a/google-cloud-speech/pom.xml b/google-cloud-speech/pom.xml
index 3e48afaad48e..1788fd4c58ab 100644
--- a/google-cloud-speech/pom.xml
+++ b/google-cloud-speech/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-speech
diff --git a/google-cloud-storage/pom.xml b/google-cloud-storage/pom.xml
index 1b7c8e86be6b..337a2d5eb044 100644
--- a/google-cloud-storage/pom.xml
+++ b/google-cloud-storage/pom.xml
@@ -12,7 +12,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-storage
diff --git a/google-cloud-trace/pom.xml b/google-cloud-trace/pom.xml
index 7a0869402104..591295d760bc 100644
--- a/google-cloud-trace/pom.xml
+++ b/google-cloud-trace/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-trace
diff --git a/google-cloud-translate/pom.xml b/google-cloud-translate/pom.xml
index 8d645b08643e..aed59fc11a2c 100644
--- a/google-cloud-translate/pom.xml
+++ b/google-cloud-translate/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-translate
diff --git a/google-cloud-vision/pom.xml b/google-cloud-vision/pom.xml
index 3b674429969e..e9349fc64c45 100644
--- a/google-cloud-vision/pom.xml
+++ b/google-cloud-vision/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     google-cloud-vision
diff --git a/google-cloud/pom.xml b/google-cloud/pom.xml
index a162e2e61f7f..f3d263910d0d 100644
--- a/google-cloud/pom.xml
+++ b/google-cloud/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-SNAPSHOT
+    0.8.1-alpha
   
   
     
diff --git a/pom.xml b/pom.xml
index 1eeb70f31789..92e7330fe555 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
   com.google.cloud
   google-cloud-pom
   pom
-  0.8.1-SNAPSHOT
+  0.8.1-alpha
   Google Cloud
   https://github.com/GoogleCloudPlatform/google-cloud-java
   
@@ -92,8 +92,8 @@
     github
     0.6.0
     1.0.3
-    0.8.1-SNAPSHOT
-    0.8.1-beta-SNAPSHOT
+    0.8.1-alpha
+    0.8.1-beta
     ${beta.version}
     google-cloud
   

From d884fc0deab53ffedb3b1b69eeb5e292e56e34b1 Mon Sep 17 00:00:00 2001
From: Graham Polley 
Date: Tue, 10 Jan 2017 14:18:41 +1100
Subject: [PATCH 07/22] Fix code snippet (wrong method name) in README.md

Original code snippet in _"Querying data"_ section:

`..while (!queryResponse.jobComplete()) {..`

This results in a compile error:

_"Cannot resolve method jobComplete()"_

The correct method is `jobCompleted()`
---
 google-cloud-bigquery/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/google-cloud-bigquery/README.md b/google-cloud-bigquery/README.md
index 616494887e3b..58fa641af081 100644
--- a/google-cloud-bigquery/README.md
+++ b/google-cloud-bigquery/README.md
@@ -189,7 +189,7 @@ QueryRequest queryRequest =
         .build();
 // Request query to be executed and wait for results
 QueryResponse queryResponse = bigquery.query(queryRequest);
-while (!queryResponse.jobComplete()) {
+while (!queryResponse.jobCompleted()) {
   Thread.sleep(1000L);
   queryResponse = bigquery.getQueryResults(queryResponse.getJobId());
 }

From 424d5b5a3b3e2ce1a4167d634d04c56abc7f5376 Mon Sep 17 00:00:00 2001
From: Garrett Jones 
Date: Mon, 9 Jan 2017 17:22:24 -0800
Subject: [PATCH 08/22] Updating version in README files. [ci skip]

---
 README.md                                                | 6 +++---
 google-cloud-compute/README.md                           | 6 +++---
 google-cloud-contrib/README.md                           | 6 +++---
 google-cloud-contrib/google-cloud-nio-examples/README.md | 4 ++--
 google-cloud-contrib/google-cloud-nio/README.md          | 6 +++---
 google-cloud-core/README.md                              | 6 +++---
 google-cloud-dns/README.md                               | 6 +++---
 google-cloud-examples/README.md                          | 6 +++---
 google-cloud-pubsub/README.md                            | 6 +++---
 google-cloud-resourcemanager/README.md                   | 6 +++---
 google-cloud-translate/README.md                         | 6 +++---
 google-cloud/README.md                                   | 6 +++---
 12 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/README.md b/README.md
index 6d423b25be1f..66ef8190bd3e 100644
--- a/README.md
+++ b/README.md
@@ -43,16 +43,16 @@ If you are using Maven, add this to your pom.xml file
 
   com.google.cloud
   google-cloud
-  0.8.0
+  0.8.1-alpha
 
 ```
 If you are using Gradle, add this to your dependencies
 ```Groovy
-compile 'com.google.cloud:google-cloud:0.8.0'
+compile 'com.google.cloud:google-cloud:0.8.1-alpha'
 ```
 If you are using SBT, add this to your dependencies
 ```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud" % "0.8.0"
+libraryDependencies += "com.google.cloud" % "google-cloud" % "0.8.1-alpha"
 ```
 
 Example Applications
diff --git a/google-cloud-compute/README.md b/google-cloud-compute/README.md
index f7c18a0eafa9..5104cfc772a6 100644
--- a/google-cloud-compute/README.md
+++ b/google-cloud-compute/README.md
@@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file
 
   com.google.cloud
   google-cloud-compute
-  0.8.0
+  0.8.1-alpha
 
 ```
 If you are using Gradle, add this to your dependencies
 ```Groovy
-compile 'com.google.cloud:google-cloud-compute:0.8.0'
+compile 'com.google.cloud:google-cloud-compute:0.8.1-alpha'
 ```
 If you are using SBT, add this to your dependencies
 ```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-compute" % "0.8.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-compute" % "0.8.1-alpha"
 ```
 
 Example Application
diff --git a/google-cloud-contrib/README.md b/google-cloud-contrib/README.md
index 8b7aa1323089..c03d0545be5b 100644
--- a/google-cloud-contrib/README.md
+++ b/google-cloud-contrib/README.md
@@ -25,16 +25,16 @@ If you are using Maven, add this to your pom.xml file
 
   com.google.cloud
   google-cloud-contrib
-  0.8.0
+  0.8.1-alpha
 
 ```
 If you are using Gradle, add this to your dependencies
 ```Groovy
-compile 'com.google.cloud:google-cloud-contrib:0.8.0'
+compile 'com.google.cloud:google-cloud-contrib:0.8.1-alpha'
 ```
 If you are using SBT, add this to your dependencies
 ```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-contrib" % "0.8.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-contrib" % "0.8.1-alpha"
 ```
 
 ### google-cloud-nio-examples
diff --git a/google-cloud-contrib/google-cloud-nio-examples/README.md b/google-cloud-contrib/google-cloud-nio-examples/README.md
index b74d11a79059..93c191c8895e 100644
--- a/google-cloud-contrib/google-cloud-nio-examples/README.md
+++ b/google-cloud-contrib/google-cloud-nio-examples/README.md
@@ -22,12 +22,12 @@ To run this example:
 4.    Run the sample with:
 
     ```
-    java -cp google-cloud-contrib/google-cloud-nio/target/google-cloud-nio-0.7.1-SNAPSHOT-shaded.jar:google-cloud-contrib/google-cloud-nio-examples/target/google-cloud-nio-examples-0.7.1-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems
+    java -cp google-cloud-contrib/google-cloud-nio/target/google-cloud-nio-0.8.2-alpha-SNAPSHOT-shaded.jar:google-cloud-contrib/google-cloud-nio-examples/target/google-cloud-nio-examples-0.8.2-alpha-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems
     ```
 
     Notice that it lists Google Cloud Storage, which it wouldn't if you ran it without the NIO jar:
     ```
-    java -cp google-cloud-contrib/google-cloud-nio-examples/target/google-cloud-nio-examples-0.7.1-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems
+    java -cp google-cloud-contrib/google-cloud-nio-examples/target/google-cloud-nio-examples-0.8.2-alpha-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems
     ```
 
 The sample doesn't have anything about Google Cloud Storage in it. It gets that ability from the NIO
diff --git a/google-cloud-contrib/google-cloud-nio/README.md b/google-cloud-contrib/google-cloud-nio/README.md
index 7d7df3b722bc..c16102a1f5c0 100644
--- a/google-cloud-contrib/google-cloud-nio/README.md
+++ b/google-cloud-contrib/google-cloud-nio/README.md
@@ -26,16 +26,16 @@ If you are using Maven, add this to your pom.xml file
 
   com.google.cloud
   google-cloud-nio
-  0.8.0
+  0.8.1-alpha
 
 ```
 If you are using Gradle, add this to your dependencies
 ```Groovy
-compile 'com.google.cloud:google-cloud-nio:0.8.0'
+compile 'com.google.cloud:google-cloud-nio:0.8.1-alpha'
 ```
 If you are using SBT, add this to your dependencies
 ```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-nio" % "0.8.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-nio" % "0.8.1-alpha"
 ```
 
 Example Applications
diff --git a/google-cloud-core/README.md b/google-cloud-core/README.md
index f711365e184b..2f5971b4557c 100644
--- a/google-cloud-core/README.md
+++ b/google-cloud-core/README.md
@@ -19,16 +19,16 @@ If you are using Maven, add this to your pom.xml file
 
   com.google.cloud
   google-cloud-core
-  0.8.0
+  0.8.1-alpha
 
 ```
 If you are using Gradle, add this to your dependencies
 ```Groovy
-compile 'com.google.cloud:google-cloud-core:0.8.0'
+compile 'com.google.cloud:google-cloud-core:0.8.1-alpha'
 ```
 If you are using SBT, add this to your dependencies
 ```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-core" % "0.8.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-core" % "0.8.1-alpha"
 ```
 
 Troubleshooting
diff --git a/google-cloud-dns/README.md b/google-cloud-dns/README.md
index 6ae180f976dd..bbc185bc1218 100644
--- a/google-cloud-dns/README.md
+++ b/google-cloud-dns/README.md
@@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file
 
   com.google.cloud
   google-cloud-dns
-  0.8.0
+  0.8.1-alpha
 
 ```
 If you are using Gradle, add this to your dependencies
 ```Groovy
-compile 'com.google.cloud:google-cloud-dns:0.8.0'
+compile 'com.google.cloud:google-cloud-dns:0.8.1-alpha'
 ```
 If you are using SBT, add this to your dependencies
 ```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-dns" % "0.8.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-dns" % "0.8.1-alpha"
 ```
 
 Example Application
diff --git a/google-cloud-examples/README.md b/google-cloud-examples/README.md
index 5dbbffcb9f5e..f9e6f2dac0b2 100644
--- a/google-cloud-examples/README.md
+++ b/google-cloud-examples/README.md
@@ -19,16 +19,16 @@ If you are using Maven, add this to your pom.xml file
 
   com.google.cloud
   google-cloud-examples
-  0.8.0
+  0.8.1-alpha
 
 ```
 If you are using Gradle, add this to your dependencies
 ```Groovy
-compile 'com.google.cloud:google-cloud-examples:0.8.0'
+compile 'com.google.cloud:google-cloud-examples:0.8.1-alpha'
 ```
 If you are using SBT, add this to your dependencies
 ```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-examples" % "0.8.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-examples" % "0.8.1-alpha"
 ```
 
 To run examples from your command line:
diff --git a/google-cloud-pubsub/README.md b/google-cloud-pubsub/README.md
index dc60848f7bed..2b7cc5c9471d 100644
--- a/google-cloud-pubsub/README.md
+++ b/google-cloud-pubsub/README.md
@@ -26,16 +26,16 @@ Add this to your pom.xml file
 
   com.google.cloud
   google-cloud-pubsub
-  0.8.0
+  0.8.1-alpha
 
 ```
 If you are using Gradle, add this to your dependencies
 ```Groovy
-compile 'com.google.cloud:google-cloud-pubsub:0.8.0'
+compile 'com.google.cloud:google-cloud-pubsub:0.8.1-alpha'
 ```
 If you are using SBT, add this to your dependencies
 ```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-pubsub" % "0.8.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-pubsub" % "0.8.1-alpha"
 ```
 
 Example Application
diff --git a/google-cloud-resourcemanager/README.md b/google-cloud-resourcemanager/README.md
index e261feb07158..6b6b3020c545 100644
--- a/google-cloud-resourcemanager/README.md
+++ b/google-cloud-resourcemanager/README.md
@@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file
 
   com.google.cloud
   google-cloud-resourcemanager
-  0.8.0
+  0.8.1-alpha
 
 ```
 If you are using Gradle, add this to your dependencies
 ```Groovy
-compile 'com.google.cloud:google-cloud-resourcemanager:0.8.0'
+compile 'com.google.cloud:google-cloud-resourcemanager:0.8.1-alpha'
 ```
 If you are using SBT, add this to your dependencies
 ```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-resourcemanager" % "0.8.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-resourcemanager" % "0.8.1-alpha"
 ```
 
 Example Application
diff --git a/google-cloud-translate/README.md b/google-cloud-translate/README.md
index 5571619bb0c2..defae571ad91 100644
--- a/google-cloud-translate/README.md
+++ b/google-cloud-translate/README.md
@@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file
 
   com.google.cloud
   google-cloud-translate
-  0.8.0
+  0.8.1-alpha
 
 ```
 If you are using Gradle, add this to your dependencies
 ```Groovy
-compile 'com.google.cloud:google-cloud-translate:0.8.0'
+compile 'com.google.cloud:google-cloud-translate:0.8.1-alpha'
 ```
 If you are using SBT, add this to your dependencies
 ```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-translate" % "0.8.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-translate" % "0.8.1-alpha"
 ```
 
 Example Application
diff --git a/google-cloud/README.md b/google-cloud/README.md
index 9da900cd8b2a..58c7a4d6756c 100644
--- a/google-cloud/README.md
+++ b/google-cloud/README.md
@@ -27,16 +27,16 @@ If you are using Maven, add this to your pom.xml file
 
   com.google.cloud
   google-cloud
-  0.8.0
+  0.8.1-alpha
 
 ```
 If you are using Gradle, add this to your dependencies
 ```Groovy
-compile 'com.google.cloud:google-cloud:0.8.0'
+compile 'com.google.cloud:google-cloud:0.8.1-alpha'
 ```
 If you are using SBT, add this to your dependencies
 ```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud" % "0.8.0"
+libraryDependencies += "com.google.cloud" % "google-cloud" % "0.8.1-alpha"
 ```
 
 Troubleshooting

From d1e1c85c93b0c53a66b60687e5accc54d416aac5 Mon Sep 17 00:00:00 2001
From: Garrett Jones 
Date: Mon, 9 Jan 2017 17:53:27 -0800
Subject: [PATCH 09/22] Update version to 0.8.2-alpha-SNAPSHOT

---
 google-cloud-bigquery/pom.xml                          | 2 +-
 google-cloud-compute/pom.xml                           | 2 +-
 google-cloud-contrib/google-cloud-nio-examples/pom.xml | 2 +-
 google-cloud-contrib/google-cloud-nio/pom.xml          | 2 +-
 google-cloud-contrib/pom.xml                           | 2 +-
 google-cloud-core/pom.xml                              | 2 +-
 google-cloud-datastore/pom.xml                         | 2 +-
 google-cloud-dns/pom.xml                               | 2 +-
 google-cloud-errorreporting/pom.xml                    | 2 +-
 google-cloud-examples/pom.xml                          | 2 +-
 google-cloud-language/pom.xml                          | 2 +-
 google-cloud-logging/pom.xml                           | 2 +-
 google-cloud-monitoring/pom.xml                        | 2 +-
 google-cloud-pubsub/pom.xml                            | 2 +-
 google-cloud-resourcemanager/pom.xml                   | 2 +-
 google-cloud-speech/pom.xml                            | 2 +-
 google-cloud-storage/pom.xml                           | 2 +-
 google-cloud-trace/pom.xml                             | 2 +-
 google-cloud-translate/pom.xml                         | 2 +-
 google-cloud-vision/pom.xml                            | 2 +-
 google-cloud/pom.xml                                   | 2 +-
 pom.xml                                                | 6 +++---
 22 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/google-cloud-bigquery/pom.xml b/google-cloud-bigquery/pom.xml
index 409ad6faf989..46ce9486a257 100644
--- a/google-cloud-bigquery/pom.xml
+++ b/google-cloud-bigquery/pom.xml
@@ -12,7 +12,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-bigquery
diff --git a/google-cloud-compute/pom.xml b/google-cloud-compute/pom.xml
index b1851df41b92..15910c065623 100644
--- a/google-cloud-compute/pom.xml
+++ b/google-cloud-compute/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-compute
diff --git a/google-cloud-contrib/google-cloud-nio-examples/pom.xml b/google-cloud-contrib/google-cloud-nio-examples/pom.xml
index f5c7fef93cbc..e3cbb6da9ed3 100644
--- a/google-cloud-contrib/google-cloud-nio-examples/pom.xml
+++ b/google-cloud-contrib/google-cloud-nio-examples/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-contrib
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-nio-examples
diff --git a/google-cloud-contrib/google-cloud-nio/pom.xml b/google-cloud-contrib/google-cloud-nio/pom.xml
index 8b475667adcd..28a6c2409464 100644
--- a/google-cloud-contrib/google-cloud-nio/pom.xml
+++ b/google-cloud-contrib/google-cloud-nio/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-contrib
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-nio
diff --git a/google-cloud-contrib/pom.xml b/google-cloud-contrib/pom.xml
index 3a1b299879ef..5fd17e4e0b74 100644
--- a/google-cloud-contrib/pom.xml
+++ b/google-cloud-contrib/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-contrib
diff --git a/google-cloud-core/pom.xml b/google-cloud-core/pom.xml
index c0bfd8d15eb8..34974a496520 100644
--- a/google-cloud-core/pom.xml
+++ b/google-cloud-core/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-core
diff --git a/google-cloud-datastore/pom.xml b/google-cloud-datastore/pom.xml
index dece795ce8a0..eedb8abfa699 100644
--- a/google-cloud-datastore/pom.xml
+++ b/google-cloud-datastore/pom.xml
@@ -12,7 +12,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-datastore
diff --git a/google-cloud-dns/pom.xml b/google-cloud-dns/pom.xml
index 207b4f8e57ff..9de45f6e6c5e 100644
--- a/google-cloud-dns/pom.xml
+++ b/google-cloud-dns/pom.xml
@@ -13,7 +13,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-dns
diff --git a/google-cloud-errorreporting/pom.xml b/google-cloud-errorreporting/pom.xml
index 480ec1beb953..025eec9f697d 100644
--- a/google-cloud-errorreporting/pom.xml
+++ b/google-cloud-errorreporting/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-errorreporting
diff --git a/google-cloud-examples/pom.xml b/google-cloud-examples/pom.xml
index 72f4f4bb975b..f754dd82e0af 100644
--- a/google-cloud-examples/pom.xml
+++ b/google-cloud-examples/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-examples
diff --git a/google-cloud-language/pom.xml b/google-cloud-language/pom.xml
index f14929eaf9cf..82466d147541 100644
--- a/google-cloud-language/pom.xml
+++ b/google-cloud-language/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-language
diff --git a/google-cloud-logging/pom.xml b/google-cloud-logging/pom.xml
index 81f381e1fad9..1fabe063ff87 100644
--- a/google-cloud-logging/pom.xml
+++ b/google-cloud-logging/pom.xml
@@ -12,7 +12,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-logging
diff --git a/google-cloud-monitoring/pom.xml b/google-cloud-monitoring/pom.xml
index 9ffaf9607666..d7513bc8791e 100644
--- a/google-cloud-monitoring/pom.xml
+++ b/google-cloud-monitoring/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-monitoring
diff --git a/google-cloud-pubsub/pom.xml b/google-cloud-pubsub/pom.xml
index 7e62030864c4..adcc405c0a83 100644
--- a/google-cloud-pubsub/pom.xml
+++ b/google-cloud-pubsub/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-pubsub
diff --git a/google-cloud-resourcemanager/pom.xml b/google-cloud-resourcemanager/pom.xml
index 17b7c544d58f..d2991adff40f 100644
--- a/google-cloud-resourcemanager/pom.xml
+++ b/google-cloud-resourcemanager/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-resourcemanager
diff --git a/google-cloud-speech/pom.xml b/google-cloud-speech/pom.xml
index 1788fd4c58ab..363aa3b54715 100644
--- a/google-cloud-speech/pom.xml
+++ b/google-cloud-speech/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-speech
diff --git a/google-cloud-storage/pom.xml b/google-cloud-storage/pom.xml
index 337a2d5eb044..335a91cb7688 100644
--- a/google-cloud-storage/pom.xml
+++ b/google-cloud-storage/pom.xml
@@ -12,7 +12,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-storage
diff --git a/google-cloud-trace/pom.xml b/google-cloud-trace/pom.xml
index 591295d760bc..e67fbc27238e 100644
--- a/google-cloud-trace/pom.xml
+++ b/google-cloud-trace/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-trace
diff --git a/google-cloud-translate/pom.xml b/google-cloud-translate/pom.xml
index aed59fc11a2c..048036efdbee 100644
--- a/google-cloud-translate/pom.xml
+++ b/google-cloud-translate/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-translate
diff --git a/google-cloud-vision/pom.xml b/google-cloud-vision/pom.xml
index e9349fc64c45..c62784a28104 100644
--- a/google-cloud-vision/pom.xml
+++ b/google-cloud-vision/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     google-cloud-vision
diff --git a/google-cloud/pom.xml b/google-cloud/pom.xml
index f3d263910d0d..2f1042fc38fb 100644
--- a/google-cloud/pom.xml
+++ b/google-cloud/pom.xml
@@ -11,7 +11,7 @@
   
     com.google.cloud
     google-cloud-pom
-    0.8.1-alpha
+    0.8.2-alpha-SNAPSHOT
   
   
     
diff --git a/pom.xml b/pom.xml
index 92e7330fe555..f42ef8a306b6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
   com.google.cloud
   google-cloud-pom
   pom
-  0.8.1-alpha
+  0.8.2-alpha-SNAPSHOT
   Google Cloud
   https://github.com/GoogleCloudPlatform/google-cloud-java
   
@@ -92,8 +92,8 @@
     github
     0.6.0
     1.0.3
-    0.8.1-alpha
-    0.8.1-beta
+    0.8.2-alpha-SNAPSHOT
+    0.8.2-beta-SNAPSHOT
     ${beta.version}
     google-cloud
   

From 824ad7753fb24ba92035bf8a7874ea9ea8aa5d87 Mon Sep 17 00:00:00 2001
From: JP Martin 
Date: Thu, 12 Jan 2017 10:10:54 -0800
Subject: [PATCH 10/22] Allow path in URIs passed to newFileSystem (#1470)

* This makes it easier for users who start with a URI describing a full
path to get a FileSystem that can work with that path, since they no
longer have to needlessly remove the path from the URI.

Note that Oracle's description of newFileSystem [1] puts no restriction
on the passed URI.

[1]
https://docs.oracle.com/javase/7/docs/api/java/nio/file/FileSystems.html#newFileSystem(java.net.URI,%20java.util.Map)
---
 .../contrib/nio/CloudStorageFileSystemProvider.java    | 10 +++++-----
 .../nio/CloudStorageFileSystemProviderTest.java        |  8 ++++++++
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/google-cloud-contrib/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/google-cloud-contrib/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
index 727327d215fd..f65549b69fec 100644
--- a/google-cloud-contrib/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
+++ b/google-cloud-contrib/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
@@ -170,14 +170,15 @@ public CloudStorageFileSystem getFileSystem(URI uri) {
   }
 
   /**
-   * Returns Cloud Storage file system, provided a URI with no path, e.g. {@code gs://bucket}.
+   * Returns Cloud Storage file system, provided a URI, e.g. {@code gs://bucket}.
+   * The URI can include a path component (that will be ignored).
    *
    * @param uri bucket and current working directory, e.g. {@code gs://bucket}
    * @param env map of configuration options, whose keys correspond to the method names of
    *     {@link CloudStorageConfiguration.Builder}. However you are not allowed to set the working
    *     directory, as that should be provided in the {@code uri}
-   * @throws IllegalArgumentException if {@code uri} specifies a user, query, fragment, or scheme is
-   *     not {@value CloudStorageFileSystem#URI_SCHEME}
+   * @throws IllegalArgumentException if {@code uri} specifies a port, user, query, or fragment, or
+   *     if scheme is not {@value CloudStorageFileSystem#URI_SCHEME}
    */
   @Override
   public CloudStorageFileSystem newFileSystem(URI uri, Map env) {
@@ -191,11 +192,10 @@ public CloudStorageFileSystem newFileSystem(URI uri, Map env) {
         CloudStorageFileSystem.URI_SCHEME, uri);
     checkArgument(
         uri.getPort() == -1
-            && isNullOrEmpty(uri.getPath())
             && isNullOrEmpty(uri.getQuery())
             && isNullOrEmpty(uri.getFragment())
             && isNullOrEmpty(uri.getUserInfo()),
-        "GCS FileSystem URIs mustn't have: port, userinfo, path, query, or fragment: %s",
+        "GCS FileSystem URIs mustn't have: port, userinfo, query, or fragment: %s",
         uri);
     CloudStorageUtil.checkBucket(uri.getHost());
     initStorage();
diff --git a/google-cloud-contrib/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/google-cloud-contrib/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
index 86e87d0e71e1..970d60217e57 100644
--- a/google-cloud-contrib/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
+++ b/google-cloud-contrib/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
@@ -54,7 +54,9 @@
 import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Unit tests for {@link CloudStorageFileSystemProvider}.
@@ -644,6 +646,12 @@ public void testProviderEquals() {
     assertThat(path1.getFileSystem().provider()).isNotEqualTo(path3.getFileSystem().provider());
   }
 
+  @Test
+  public void testNewFileSystem() throws IOException {
+    Map env = new HashMap<>();
+    FileSystems.newFileSystem(URI.create("gs://bucket/path/to/file"), env);
+  }
+
   private static CloudStorageConfiguration permitEmptyPathComponents(boolean value) {
     return CloudStorageConfiguration.builder().permitEmptyPathComponents(value).build();
   }

From d4d494d5bd51fee0134fb42cc9b82a5586b717dd Mon Sep 17 00:00:00 2001
From: garrettjonesgoogle 
Date: Thu, 12 Jan 2017 20:34:51 -0800
Subject: [PATCH 11/22] Preventing logging re-entrance at FINE level (#1523)

* Preventing logging re-entrance at FINE level

Also:
* Reducing the scope of synchronized blocks
* Removing logger exclusions except for Http2FrameLogger
---
 .../google/cloud/logging/LoggingHandler.java  | 110 ++++++++++-------
 .../spi/v2/LoggingServiceV2Client.java        |   3 +
 .../spi/v2/LoggingServiceV2Settings.java      |  96 ++++++++++++++-
 .../cloud/logging/LoggingHandlerTest.java     | 112 +++++++-----------
 4 files changed, 204 insertions(+), 117 deletions(-)

diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java
index e7168d6da678..3bdc301c624a 100644
--- a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java
+++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java
@@ -22,11 +22,8 @@
 import com.google.cloud.logging.Logging.WriteOption;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Set;
 import java.util.logging.ErrorManager;
 import java.util.logging.Filter;
 import java.util.logging.Formatter;
@@ -93,13 +90,13 @@ public class LoggingHandler extends Handler {
   private static final String HANDLERS_PROPERTY = "handlers";
   private static final String ROOT_LOGGER_NAME = "";
   private static final String[] NO_HANDLERS = new String[0];
-  private static final Set EXCLUDED_LOGGERS = ImmutableSet.of("io.grpc", "io.netty",
-      "com.google.api.client.http", "sun.net.www.protocol.http");
+
+  private static final ThreadLocal inPublishCall = new ThreadLocal<>();
 
   private final LoggingOptions options;
-  private final List buffer = new LinkedList<>();
   private final WriteOption[] writeOptions;
-  private Logging logging;
+  private List buffer = new LinkedList<>();
+  private volatile Logging logging;
   private Level flushLevel;
   private long flushSize;
 
@@ -148,31 +145,6 @@ public LoggingHandler(String log, LoggingOptions options, MonitoredResource moni
     String logName = firstNonNull(log, helper.getProperty(className + ".log", "java.log"));
     MonitoredResource resource = firstNonNull(monitoredResource, getDefaultResource());
     writeOptions = new WriteOption[]{WriteOption.logName(logName), WriteOption.resource(resource)};
-    maskLoggers();
-  }
-
-  private static void maskLoggers() {
-    for (String loggerName : EXCLUDED_LOGGERS) {
-      Logger logger = Logger.getLogger(loggerName);
-      // We remove the Clould Logging handler if it has been registered for a logger that should be
-      // masked
-      List loggingHandlers = getLoggingHandlers(logger);
-      for (LoggingHandler loggingHandler : loggingHandlers) {
-        logger.removeHandler(loggingHandler);
-      }
-      // We mask ancestors if they have a Stackdriver Logging Handler registered
-      Logger currentLogger = logger;
-      Logger ancestor = currentLogger.getParent();
-      boolean masked = false;
-      while (ancestor != null && !masked) {
-        if (hasLoggingHandler(ancestor)) {
-          currentLogger.setUseParentHandlers(false);
-          masked = true;
-        }
-        currentLogger = ancestor;
-        ancestor = ancestor.getParent();
-      }
-    }
   }
 
   private static List getLoggingHandlers(Logger logger) {
@@ -272,23 +244,55 @@ Formatter getFormatterProperty(String name, Formatter defaultValue) {
    */
   Logging getLogging() {
     if (logging == null) {
-      logging = options.getService();
+      synchronized (this) {
+        if (logging == null) {
+          logging = options.getService();
+        }
+      }
     }
     return logging;
   }
 
   @Override
-  public synchronized void publish(LogRecord record) {
+  public void publish(LogRecord record) {
     // check that the log record should be logged
     if (!isLoggable(record)) {
       return;
     }
-    LogEntry entry = entryFor(record);
-    if (entry != null) {
-      buffer.add(entry);
+
+    // HACK warning: this logger doesn't work like normal loggers; the log calls are issued
+    // from another class instead of by itself, so it can't be configured off like normal
+    // loggers. We have to check the source class name instead.
+    if ("io.netty.handler.codec.http2.Http2FrameLogger".equals(record.getSourceClassName())) {
+      return;
     }
-    if (buffer.size() >= flushSize || record.getLevel().intValue() >= flushLevel.intValue()) {
-      flush();
+
+    if (inPublishCall.get() != null) {
+      // ignore all logs generated in the course of logging through this handler
+      return;
+    }
+    inPublishCall.set(true);
+
+    try {
+      LogEntry entry = entryFor(record);
+
+      List flushBuffer = null;
+      WriteOption[] flushWriteOptions = null;
+
+      synchronized (this) {
+        if (entry != null) {
+          buffer.add(entry);
+        }
+        if (buffer.size() >= flushSize || record.getLevel().intValue() >= flushLevel.intValue()) {
+          flushBuffer = buffer;
+          flushWriteOptions = writeOptions;
+          buffer = new LinkedList<>();
+        }
+      }
+
+      flush(flushBuffer, flushWriteOptions);
+    } finally {
+      inPublishCall.remove();
     }
   }
 
@@ -350,18 +354,35 @@ private static Severity severityFor(Level level) {
    * how entries should be written.
    */
   void write(List entries, WriteOption... options) {
-    getLogging().write(entries, options);
+    getLogging().writeAsync(entries, options);
   }
 
   @Override
-  public synchronized void flush() {
+  public void flush() {
+    List flushBuffer;
+    WriteOption[] flushWriteOptions;
+
+    synchronized (this) {
+      if (buffer.isEmpty()) {
+        return;
+      }
+      flushBuffer = buffer;
+      flushWriteOptions = writeOptions;
+      buffer = new LinkedList<>();
+    }
+
+    flush(flushBuffer, flushWriteOptions);
+  }
+
+  private void flush(List flushBuffer, WriteOption[] flushWriteOptions) {
+    if (flushBuffer == null) {
+      return;
+    }
     try {
-      write(buffer, writeOptions);
+      write(flushBuffer, flushWriteOptions);
     } catch (Exception ex) {
       // writing can fail but we should not throw an exception, we report the error instead
       reportError(null, ex, ErrorManager.FLUSH_FAILURE);
-    } finally {
-      buffer.clear();
     }
   }
 
@@ -407,6 +428,5 @@ public synchronized long setFlushSize(long flushSize) {
    */
   public static void addHandler(Logger logger, LoggingHandler handler) {
     logger.addHandler(handler);
-    maskLoggers();
   }
 }
diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Client.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Client.java
index fd83a4e2471e..502d6b1d950e 100644
--- a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Client.java
+++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Client.java
@@ -151,6 +151,9 @@ protected LoggingServiceV2Client(LoggingServiceV2Settings settings) throws IOExc
         UnaryCallable.create(settings.deleteLogSettings(), this.channel, this.executor);
     this.writeLogEntriesCallable =
         UnaryCallable.create(settings.writeLogEntriesSettings(), this.channel, this.executor);
+    if (settings.writeLogEntriesSettings().getBundlerFactory() != null) {
+      closeables.add(settings.writeLogEntriesSettings().getBundlerFactory());
+    }
     this.listLogEntriesCallable =
         UnaryCallable.create(settings.listLogEntriesSettings(), this.channel, this.executor);
     this.listLogEntriesPagedCallable =
diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Settings.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Settings.java
index e854c3fcf872..9ea30ed25120 100644
--- a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Settings.java
+++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Settings.java
@@ -22,6 +22,9 @@
 import com.google.api.MonitoredResourceDescriptor;
 import com.google.api.gax.core.GoogleCredentialsProvider;
 import com.google.api.gax.core.RetrySettings;
+import com.google.api.gax.grpc.BundlingCallSettings;
+import com.google.api.gax.grpc.BundlingDescriptor;
+import com.google.api.gax.grpc.BundlingSettings;
 import com.google.api.gax.grpc.CallContext;
 import com.google.api.gax.grpc.ChannelProvider;
 import com.google.api.gax.grpc.ClientSettings;
@@ -31,6 +34,7 @@
 import com.google.api.gax.grpc.PagedCallSettings;
 import com.google.api.gax.grpc.PagedListDescriptor;
 import com.google.api.gax.grpc.PagedListResponseFactory;
+import com.google.api.gax.grpc.RequestIssuer;
 import com.google.api.gax.grpc.SimpleCallSettings;
 import com.google.api.gax.grpc.UnaryCallSettings;
 import com.google.api.gax.grpc.UnaryCallable;
@@ -54,6 +58,9 @@
 import com.google.protobuf.ExperimentalApi;
 import io.grpc.Status;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import javax.annotation.Generated;
 import org.joda.time.Duration;
 
@@ -103,7 +110,7 @@ public class LoggingServiceV2Settings extends ClientSettings {
           .build();
 
   private final SimpleCallSettings deleteLogSettings;
-  private final SimpleCallSettings
+  private final BundlingCallSettings
       writeLogEntriesSettings;
   private final PagedCallSettings<
           ListLogEntriesRequest, ListLogEntriesResponse, ListLogEntriesPagedResponse>
@@ -121,7 +128,7 @@ public SimpleCallSettings deleteLogSettings() {
   }
 
   /** Returns the object with the settings used for calls to writeLogEntries. */
-  public SimpleCallSettings
+  public BundlingCallSettings
       writeLogEntriesSettings() {
     return writeLogEntriesSettings;
   }
@@ -368,12 +375,80 @@ public ListLogsPagedResponse createPagedListResponse(
             }
           };
 
+  private static final BundlingDescriptor
+      WRITE_LOG_ENTRIES_BUNDLING_DESC =
+          new BundlingDescriptor() {
+            @Override
+            public String getBundlePartitionKey(WriteLogEntriesRequest request) {
+              return request.getLogName()
+                  + "|"
+                  + request.getResource()
+                  + "|"
+                  + request.getLabels()
+                  + "|";
+            }
+
+            @Override
+            public WriteLogEntriesRequest mergeRequests(
+                Collection requests) {
+              WriteLogEntriesRequest firstRequest = requests.iterator().next();
+
+              List elements = new ArrayList<>();
+              for (WriteLogEntriesRequest request : requests) {
+                elements.addAll(request.getEntriesList());
+              }
+
+              WriteLogEntriesRequest bundleRequest =
+                  WriteLogEntriesRequest.newBuilder()
+                      .setLogName(firstRequest.getLogName())
+                      .setResource(firstRequest.getResource())
+                      .putAllLabels(firstRequest.getLabels())
+                      .addAllEntries(elements)
+                      .build();
+              return bundleRequest;
+            }
+
+            @Override
+            public void splitResponse(
+                WriteLogEntriesResponse bundleResponse,
+                Collection>
+                    bundle) {
+              int bundleMessageIndex = 0;
+              for (RequestIssuer responder :
+                  bundle) {
+                WriteLogEntriesResponse response = WriteLogEntriesResponse.newBuilder().build();
+                responder.setResponse(response);
+              }
+            }
+
+            @Override
+            public void splitException(
+                Throwable throwable,
+                Collection>
+                    bundle) {
+              for (RequestIssuer responder :
+                  bundle) {
+                responder.setException(throwable);
+              }
+            }
+
+            @Override
+            public long countElements(WriteLogEntriesRequest request) {
+              return request.getEntriesCount();
+            }
+
+            @Override
+            public long countBytes(WriteLogEntriesRequest request) {
+              return request.getSerializedSize();
+            }
+          };
+
   /** Builder for LoggingServiceV2Settings. */
   public static class Builder extends ClientSettings.Builder {
     private final ImmutableList unaryMethodSettingsBuilders;
 
     private final SimpleCallSettings.Builder deleteLogSettings;
-    private final SimpleCallSettings.Builder
+    private final BundlingCallSettings.Builder
         writeLogEntriesSettings;
     private final PagedCallSettings.Builder<
             ListLogEntriesRequest, ListLogEntriesResponse, ListLogEntriesPagedResponse>
@@ -433,7 +508,9 @@ private Builder() {
       deleteLogSettings = SimpleCallSettings.newBuilder(LoggingServiceV2Grpc.METHOD_DELETE_LOG);
 
       writeLogEntriesSettings =
-          SimpleCallSettings.newBuilder(LoggingServiceV2Grpc.METHOD_WRITE_LOG_ENTRIES);
+          BundlingCallSettings.newBuilder(
+                  LoggingServiceV2Grpc.METHOD_WRITE_LOG_ENTRIES, WRITE_LOG_ENTRIES_BUNDLING_DESC)
+              .setBundlingSettingsBuilder(BundlingSettings.newBuilder());
 
       listLogEntriesSettings =
           PagedCallSettings.newBuilder(
@@ -465,6 +542,15 @@ private static Builder createDefault() {
           .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("idempotent"))
           .setRetrySettingsBuilder(RETRY_PARAM_DEFINITIONS.get("default"));
 
+      builder
+          .writeLogEntriesSettings()
+          .getBundlingSettingsBuilder()
+          .setElementCountThreshold(1)
+          .setElementCountLimit(1000)
+          .setRequestByteThreshold(1024)
+          .setRequestByteLimit(10485760)
+          .setDelayThreshold(Duration.millis(10))
+          .setBlockingCallCountThreshold(1);
       builder
           .writeLogEntriesSettings()
           .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("non_idempotent"))
@@ -537,7 +623,7 @@ public SimpleCallSettings.Builder deleteLogSettings() {
     }
 
     /** Returns the builder for the settings used for calls to writeLogEntries. */
-    public SimpleCallSettings.Builder
+    public BundlingCallSettings.Builder
         writeLogEntriesSettings() {
       return writeLogEntriesSettings;
     }
diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java
index e75c981376f9..e48b2051cd5d 100644
--- a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java
+++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java
@@ -16,28 +16,22 @@
 
 package com.google.cloud.logging;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
 import com.google.cloud.MonitoredResource;
 import com.google.cloud.logging.Logging.WriteOption;
 import com.google.cloud.logging.Payload.StringPayload;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-
-import org.easymock.EasyMock;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
+import com.google.common.util.concurrent.Futures;
 import java.util.logging.ErrorManager;
 import java.util.logging.Formatter;
 import java.util.logging.Handler;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import java.util.logging.Logger;
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 public class LoggingHandlerTest {
 
@@ -138,45 +132,45 @@ public void afterClass() {
   public void testPublishLevels() {
     EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes();
     EasyMock.expect(options.getService()).andReturn(logging);
-    logging.write(ImmutableList.of(FINEST_ENTRY), WriteOption.logName(LOG_NAME),
+    logging.writeAsync(ImmutableList.of(FINEST_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
-    logging.write(ImmutableList.of(FINER_ENTRY), WriteOption.logName(LOG_NAME),
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
+    logging.writeAsync(ImmutableList.of(FINER_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
-    logging.write(ImmutableList.of(FINE_ENTRY), WriteOption.logName(LOG_NAME),
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
+    logging.writeAsync(ImmutableList.of(FINE_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
-    logging.write(ImmutableList.of(CONFIG_ENTRY), WriteOption.logName(LOG_NAME),
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
+    logging.writeAsync(ImmutableList.of(CONFIG_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
-    logging.write(ImmutableList.of(INFO_ENTRY), WriteOption.logName(LOG_NAME),
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
+    logging.writeAsync(ImmutableList.of(INFO_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
-    logging.write(ImmutableList.of(WARNING_ENTRY), WriteOption.logName(LOG_NAME),
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
+    logging.writeAsync(ImmutableList.of(WARNING_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
-    logging.write(ImmutableList.of(SEVERE_ENTRY), WriteOption.logName(LOG_NAME),
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
+    logging.writeAsync(ImmutableList.of(SEVERE_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
-    logging.write(ImmutableList.of(DEBUG_ENTRY), WriteOption.logName(LOG_NAME),
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
+    logging.writeAsync(ImmutableList.of(DEBUG_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
-    logging.write(ImmutableList.of(NOTICE_ENTRY), WriteOption.logName(LOG_NAME),
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
+    logging.writeAsync(ImmutableList.of(NOTICE_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
-    logging.write(ImmutableList.of(ERROR_ENTRY), WriteOption.logName(LOG_NAME),
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
+    logging.writeAsync(ImmutableList.of(ERROR_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
-    logging.write(ImmutableList.of(CRITICAL_ENTRY), WriteOption.logName(LOG_NAME),
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
+    logging.writeAsync(ImmutableList.of(CRITICAL_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
-    logging.write(ImmutableList.of(ALERT_ENTRY), WriteOption.logName(LOG_NAME),
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
+    logging.writeAsync(ImmutableList.of(ALERT_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
-    logging.write(ImmutableList.of(EMERGENCY_ENTRY), WriteOption.logName(LOG_NAME),
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
+    logging.writeAsync(ImmutableList.of(EMERGENCY_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
     EasyMock.replay(options, logging);
     Handler handler = new LoggingHandler(LOG_NAME, options);
     handler.setLevel(Level.ALL);
@@ -203,9 +197,9 @@ public void testPublishCustomResource() {
     EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes();
     EasyMock.expect(options.getService()).andReturn(logging);
     MonitoredResource resource = MonitoredResource.of("custom", ImmutableMap.of());
-    logging.write(ImmutableList.of(FINEST_ENTRY), WriteOption.logName(LOG_NAME),
+    logging.writeAsync(ImmutableList.of(FINEST_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(resource));
-    EasyMock.expectLastCall();
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
     EasyMock.replay(options, logging);
     Handler handler = new LoggingHandler(LOG_NAME, options, resource);
     handler.setLevel(Level.ALL);
@@ -218,13 +212,13 @@ public void testReportFlushError() {
     EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes();
     EasyMock.expect(options.getService()).andReturn(logging);
     RuntimeException ex = new RuntimeException();
-    logging.write(ImmutableList.of(FINEST_ENTRY), WriteOption.logName(LOG_NAME),
+    logging.writeAsync(ImmutableList.of(FINEST_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
     EasyMock.expectLastCall().andThrow(ex);
     EasyMock.replay(options, logging);
     ErrorManager errorManager = EasyMock.createStrictMock(ErrorManager.class);
     errorManager.error(null, ex, ErrorManager.FLUSH_FAILURE);
-    EasyMock.expectLastCall();
+    EasyMock.expectLastCall().once();
     EasyMock.replay(errorManager);
     Handler handler = new LoggingHandler(LOG_NAME, options);
     handler.setLevel(Level.ALL);
@@ -242,7 +236,7 @@ public void testReportFormatError() {
     RuntimeException ex = new RuntimeException();
     ErrorManager errorManager = EasyMock.createStrictMock(ErrorManager.class);
     errorManager.error(null, ex, ErrorManager.FORMAT_FAILURE);
-    EasyMock.expectLastCall();
+    EasyMock.expectLastCall().once();
     LogRecord record = new LogRecord(Level.FINEST, MESSAGE);
     EasyMock.expect(formatter.format(record)).andThrow(ex);
     EasyMock.replay(errorManager, formatter);
@@ -258,9 +252,9 @@ public void testReportFormatError() {
   public void testFlushSize() {
     EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes();
     EasyMock.expect(options.getService()).andReturn(logging);
-    logging.write(ImmutableList.of(FINEST_ENTRY, FINER_ENTRY, FINE_ENTRY, CONFIG_ENTRY, INFO_ENTRY,
+    logging.writeAsync(ImmutableList.of(FINEST_ENTRY, FINER_ENTRY, FINE_ENTRY, CONFIG_ENTRY, INFO_ENTRY,
         WARNING_ENTRY), WriteOption.logName(LOG_NAME), WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
     EasyMock.replay(options, logging);
     LoggingHandler handler = new LoggingHandler(LOG_NAME, options);
     handler.setLevel(Level.ALL);
@@ -278,10 +272,10 @@ public void testFlushSize() {
   public void testFlushLevel() {
     EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes();
     EasyMock.expect(options.getService()).andReturn(logging);
-    logging.write(ImmutableList.of(FINEST_ENTRY, FINER_ENTRY, FINE_ENTRY, CONFIG_ENTRY, INFO_ENTRY,
+    logging.writeAsync(ImmutableList.of(FINEST_ENTRY, FINER_ENTRY, FINE_ENTRY, CONFIG_ENTRY, INFO_ENTRY,
         WARNING_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
     EasyMock.replay(options, logging);
     LoggingHandler handler = new LoggingHandler(LOG_NAME, options);
     handler.setLevel(Level.ALL);
@@ -300,9 +294,9 @@ public void testFlushLevel() {
   public void testAddHandler() {
     EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes();
     EasyMock.expect(options.getService()).andReturn(logging);
-    logging.write(ImmutableList.of(FINEST_ENTRY), WriteOption.logName(LOG_NAME),
+    logging.writeAsync(ImmutableList.of(FINEST_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
     EasyMock.replay(options, logging);
     LoggingHandler handler = new LoggingHandler(LOG_NAME, options);
     handler.setLevel(Level.ALL);
@@ -313,31 +307,15 @@ public void testAddHandler() {
     logger.finest(MESSAGE);
   }
 
-  @Test
-  public void testMaskLoggers() {
-    EasyMock.expect(options.getProjectId()).andReturn(PROJECT);
-    EasyMock.replay(options, logging);
-    LoggingHandler handler = new LoggingHandler(LOG_NAME, options);
-    Logger logger = Logger.getLogger("com.google");
-    Logger maskedLogger = Logger.getLogger("com.google.api.client.http");
-    maskedLogger.addHandler(handler);
-    assertTrue(maskedLogger.getUseParentHandlers());
-    assertSame(logger, maskedLogger.getParent());
-    LoggingHandler.addHandler(logger, handler);
-    assertFalse(maskedLogger.getUseParentHandlers());
-    assertEquals(0, maskedLogger.getHandlers().length);
-    logger.removeHandler(handler);
-  }
-
   @Test
   public void testClose() throws Exception {
     EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes();
     EasyMock.expect(options.getService()).andReturn(logging);
-    logging.write(ImmutableList.of(FINEST_ENTRY), WriteOption.logName(LOG_NAME),
+    logging.writeAsync(ImmutableList.of(FINEST_ENTRY), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE));
-    EasyMock.expectLastCall();
+    EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
     logging.close();
-    EasyMock.expectLastCall();
+    EasyMock.expectLastCall().once();
     EasyMock.replay(options, logging);
     Handler handler = new LoggingHandler(LOG_NAME, options);
     handler.setLevel(Level.ALL);

From f495c23cd8e6c74d13ca91767e05f8412fb84837 Mon Sep 17 00:00:00 2001
From: JP Martin 
Date: Fri, 13 Jan 2017 11:48:56 -0800
Subject: [PATCH 12/22] Add a PathMatcher for CloudStorageFileSystem (#1469)

Add a test, as well.
We reuse the default PathMatcher since it does a fine job of globbing files.
---
 .../contrib/nio/CloudStorageFileSystem.java   |  7 ++----
 .../nio/CloudStorageFileSystemTest.java       | 24 +++++++++++++++++++
 2 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/google-cloud-contrib/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java b/google-cloud-contrib/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java
index 60a39fb5a817..4f031c92f740 100644
--- a/google-cloud-contrib/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java
+++ b/google-cloud-contrib/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java
@@ -27,6 +27,7 @@
 import java.net.URISyntaxException;
 import java.nio.file.FileStore;
 import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
 import java.nio.file.Path;
 import java.nio.file.PathMatcher;
 import java.nio.file.WatchService;
@@ -210,13 +211,9 @@ public Set supportedFileAttributeViews() {
     return SUPPORTED_VIEWS;
   }
 
-  /**
-   * Throws {@link UnsupportedOperationException} because this feature hasn't been implemented yet.
-   */
   @Override
   public PathMatcher getPathMatcher(String syntaxAndPattern) {
-    // TODO(#813): Implement me.
-    throw new UnsupportedOperationException();
+    return FileSystems.getDefault().getPathMatcher(syntaxAndPattern);
   }
 
   /**
diff --git a/google-cloud-contrib/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java b/google-cloud-contrib/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
index ec856447b96a..d7d5b346c376 100644
--- a/google-cloud-contrib/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
+++ b/google-cloud-contrib/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
@@ -34,6 +34,7 @@
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.PathMatcher;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
@@ -159,4 +160,27 @@ public void testListFiles() throws IOException {
       assertThat(got).containsExactlyElementsIn(goodPaths);
     }
   }
+
+  @Test
+  public void testMatcher() throws IOException {
+    try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) {
+      String pattern1 = "glob:*.java";
+      PathMatcher javaFileMatcher = fs.getPathMatcher(pattern1);
+      assertMatches(fs, javaFileMatcher, "a.java", true);
+      assertMatches(fs, javaFileMatcher, "a.text", false);
+      assertMatches(fs, javaFileMatcher, "folder/c.java", true);
+      assertMatches(fs, javaFileMatcher, "d", false);
+      
+      String pattern2 = "glob:*.{java,text}";
+      PathMatcher javaAndTextFileMatcher = fs.getPathMatcher(pattern2);
+      assertMatches(fs, javaAndTextFileMatcher, "a.java", true);
+      assertMatches(fs, javaAndTextFileMatcher, "a.text", true);
+      assertMatches(fs, javaAndTextFileMatcher, "folder/c.java", true);
+      assertMatches(fs, javaAndTextFileMatcher, "d", false);
+    }
+  }
+
+  private void assertMatches(FileSystem fs, PathMatcher matcher, String toMatch, boolean expected) {
+    assertThat(matcher.matches(fs.getPath(toMatch).getFileName())).isEqualTo(expected);
+  }
 }

From 0010dd8ed35c605013aec8c95924c4855df7e1ed Mon Sep 17 00:00:00 2001
From: Greg Wilkins 
Date: Thu, 19 Jan 2017 04:18:05 +1100
Subject: [PATCH 13/22] Set timestamp from LogRecord (#1533)

---
 .../google/cloud/logging/LoggingHandler.java  |  1 +
 .../logging/AsyncLoggingHandlerTest.java      |  5 +-
 .../cloud/logging/LoggingHandlerTest.java     | 87 ++++++++++++-------
 3 files changed, 61 insertions(+), 32 deletions(-)

diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java
index 3bdc301c624a..d497f6e1b70b 100644
--- a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java
+++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java
@@ -309,6 +309,7 @@ private LogEntry entryFor(LogRecord record) {
     LogEntry.Builder builder = LogEntry.newBuilder(Payload.StringPayload.of(payload))
         .addLabel("levelName", level.getName())
         .addLabel("levelValue", String.valueOf(level.intValue()))
+        .setTimestamp(record.getMillis())
         .setSeverity(severityFor(level));
     enhanceLogEntry(builder, record);
     return builder.build();
diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/AsyncLoggingHandlerTest.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/AsyncLoggingHandlerTest.java
index 495430c23d75..8ea02628309f 100644
--- a/google-cloud-logging/src/test/java/com/google/cloud/logging/AsyncLoggingHandlerTest.java
+++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/AsyncLoggingHandlerTest.java
@@ -65,6 +65,7 @@ public void testPublish() {
         .setSeverity(Severity.DEBUG)
         .addLabel("levelName", "FINEST")
         .addLabel("levelValue", String.valueOf(Level.FINEST.intValue()))
+        .setTimestamp(123456789L)
         .build();
     EasyMock.expect(logging.writeAsync(ImmutableList.of(entry), WriteOption.logName(LOG_NAME),
         WriteOption.resource(DEFAULT_RESOURCE))).andReturn(FUTURE);
@@ -72,6 +73,8 @@ public void testPublish() {
     Handler handler = new AsyncLoggingHandler(LOG_NAME, options);
     handler.setLevel(Level.ALL);
     handler.setFormatter(new TestFormatter());
-    handler.publish(new LogRecord(Level.FINEST, MESSAGE));
+    LogRecord record = new LogRecord(Level.FINEST, MESSAGE);
+    record.setMillis(123456789L);
+    handler.publish(record);
   }
 }
diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java
index e48b2051cd5d..a1c1697d18af 100644
--- a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java
+++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java
@@ -44,66 +44,79 @@ public class LoggingHandlerTest {
       .setSeverity(Severity.DEBUG)
       .addLabel("levelName", "FINEST")
       .addLabel("levelValue", String.valueOf(Level.FINEST.intValue()))
+      .setTimestamp(123456789L)
       .build();
   private static final LogEntry FINER_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
       .setSeverity(Severity.DEBUG)
       .addLabel("levelName", "FINER")
       .addLabel("levelValue", String.valueOf(Level.FINER.intValue()))
+      .setTimestamp(123456789L)
       .build();
   private static final LogEntry FINE_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
       .setSeverity(Severity.DEBUG)
       .addLabel("levelName", "FINE")
       .addLabel("levelValue", String.valueOf(Level.FINE.intValue()))
+      .setTimestamp(123456789L)
       .build();
   private static final LogEntry CONFIG_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
       .setSeverity(Severity.INFO)
       .addLabel("levelName", "CONFIG")
       .addLabel("levelValue", String.valueOf(Level.CONFIG.intValue()))
+      .setTimestamp(123456789L)
       .build();
   private static final LogEntry INFO_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
       .setSeverity(Severity.INFO)
       .addLabel("levelName", "INFO")
       .addLabel("levelValue", String.valueOf(Level.INFO.intValue()))
+      .setTimestamp(123456789L)
       .build();
   private static final LogEntry WARNING_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
       .setSeverity(Severity.WARNING)
       .addLabel("levelName", "WARNING")
       .addLabel("levelValue", String.valueOf(Level.WARNING.intValue()))
+      .setTimestamp(123456789L)
       .build();
   private static final LogEntry SEVERE_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
       .setSeverity(Severity.ERROR)
       .addLabel("levelName", "SEVERE")
       .addLabel("levelValue", String.valueOf(Level.SEVERE.intValue()))
+      .setTimestamp(123456789L)
       .build();
   private static final LogEntry DEBUG_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
       .setSeverity(Severity.DEBUG)
       .addLabel("levelName", "DEBUG")
       .addLabel("levelValue", String.valueOf(LoggingLevel.DEBUG.intValue()))
+      .setTimestamp(123456789L)
       .build();
   private static final LogEntry NOTICE_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
       .setSeverity(Severity.NOTICE)
       .addLabel("levelName", "NOTICE")
       .addLabel("levelValue", String.valueOf(LoggingLevel.NOTICE.intValue()))
+      .setTimestamp(123456789L)
       .build();
   private static final LogEntry ERROR_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
       .setSeverity(Severity.ERROR)
       .addLabel("levelName", "ERROR")
       .addLabel("levelValue", String.valueOf(LoggingLevel.ERROR.intValue()))
+      .setTimestamp(123456789L)
       .build();
   private static final LogEntry CRITICAL_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
       .setSeverity(Severity.CRITICAL)
       .addLabel("levelName", "CRITICAL")
       .addLabel("levelValue", String.valueOf(LoggingLevel.CRITICAL.intValue()))
+      .setTimestamp(123456789L)
       .build();
   private static final LogEntry ALERT_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
       .setSeverity(Severity.ALERT)
       .addLabel("levelName", "ALERT")
       .addLabel("levelValue", String.valueOf(LoggingLevel.ALERT.intValue()))
+      .setTimestamp(123456789L)
       .build();
   private static final LogEntry EMERGENCY_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE))
       .setSeverity(Severity.EMERGENCY)
       .addLabel("levelName", "EMERGENCY")
       .addLabel("levelValue", String.valueOf(LoggingLevel.EMERGENCY.intValue()))
+      .setTimestamp(123456789L)
       .build();
 
   private Logging logging;
@@ -127,6 +140,13 @@ public void setUp() {
   public void afterClass() {
     EasyMock.verify(logging, options);
   }
+  
+
+  private static LogRecord newLogRecord(Level level, String message) {
+    LogRecord record = new LogRecord(level, message);
+    record.setMillis(123456789L);
+    return record;
+  }
 
   @Test
   public void testPublishLevels() {
@@ -176,20 +196,20 @@ public void testPublishLevels() {
     handler.setLevel(Level.ALL);
     handler.setFormatter(new TestFormatter());
     // default levels
-    handler.publish(new LogRecord(Level.FINEST, MESSAGE));
-    handler.publish(new LogRecord(Level.FINER, MESSAGE));
-    handler.publish(new LogRecord(Level.FINE, MESSAGE));
-    handler.publish(new LogRecord(Level.CONFIG, MESSAGE));
-    handler.publish(new LogRecord(Level.INFO, MESSAGE));
-    handler.publish(new LogRecord(Level.WARNING, MESSAGE));
-    handler.publish(new LogRecord(Level.SEVERE, MESSAGE));
+    handler.publish(newLogRecord(Level.FINEST, MESSAGE));
+    handler.publish(newLogRecord(Level.FINER, MESSAGE));
+    handler.publish(newLogRecord(Level.FINE, MESSAGE));
+    handler.publish(newLogRecord(Level.CONFIG, MESSAGE));
+    handler.publish(newLogRecord(Level.INFO, MESSAGE));
+    handler.publish(newLogRecord(Level.WARNING, MESSAGE));
+    handler.publish(newLogRecord(Level.SEVERE, MESSAGE));
     // Logging levels
-    handler.publish(new LogRecord(LoggingLevel.DEBUG, MESSAGE));
-    handler.publish(new LogRecord(LoggingLevel.NOTICE, MESSAGE));
-    handler.publish(new LogRecord(LoggingLevel.ERROR, MESSAGE));
-    handler.publish(new LogRecord(LoggingLevel.CRITICAL, MESSAGE));
-    handler.publish(new LogRecord(LoggingLevel.ALERT, MESSAGE));
-    handler.publish(new LogRecord(LoggingLevel.EMERGENCY, MESSAGE));
+    handler.publish(newLogRecord(LoggingLevel.DEBUG, MESSAGE));
+    handler.publish(newLogRecord(LoggingLevel.NOTICE, MESSAGE));
+    handler.publish(newLogRecord(LoggingLevel.ERROR, MESSAGE));
+    handler.publish(newLogRecord(LoggingLevel.CRITICAL, MESSAGE));
+    handler.publish(newLogRecord(LoggingLevel.ALERT, MESSAGE));
+    handler.publish(newLogRecord(LoggingLevel.EMERGENCY, MESSAGE));
   }
 
   @Test
@@ -204,7 +224,7 @@ public void testPublishCustomResource() {
     Handler handler = new LoggingHandler(LOG_NAME, options, resource);
     handler.setLevel(Level.ALL);
     handler.setFormatter(new TestFormatter());
-    handler.publish(new LogRecord(Level.FINEST, MESSAGE));
+    handler.publish(newLogRecord(Level.FINEST, MESSAGE));
   }
 
   @Test
@@ -224,7 +244,7 @@ public void testReportFlushError() {
     handler.setLevel(Level.ALL);
     handler.setErrorManager(errorManager);
     handler.setFormatter(new TestFormatter());
-    handler.publish(new LogRecord(Level.FINEST, MESSAGE));
+    handler.publish(newLogRecord(Level.FINEST, MESSAGE));
     EasyMock.verify(errorManager);
   }
 
@@ -237,7 +257,7 @@ public void testReportFormatError() {
     ErrorManager errorManager = EasyMock.createStrictMock(ErrorManager.class);
     errorManager.error(null, ex, ErrorManager.FORMAT_FAILURE);
     EasyMock.expectLastCall().once();
-    LogRecord record = new LogRecord(Level.FINEST, MESSAGE);
+    LogRecord record = newLogRecord(Level.FINEST, MESSAGE);
     EasyMock.expect(formatter.format(record)).andThrow(ex);
     EasyMock.replay(errorManager, formatter);
     Handler handler = new LoggingHandler(LOG_NAME, options);
@@ -260,12 +280,12 @@ public void testFlushSize() {
     handler.setLevel(Level.ALL);
     handler.setFlushSize(6);
     handler.setFormatter(new TestFormatter());
-    handler.publish(new LogRecord(Level.FINEST, MESSAGE));
-    handler.publish(new LogRecord(Level.FINER, MESSAGE));
-    handler.publish(new LogRecord(Level.FINE, MESSAGE));
-    handler.publish(new LogRecord(Level.CONFIG, MESSAGE));
-    handler.publish(new LogRecord(Level.INFO, MESSAGE));
-    handler.publish(new LogRecord(Level.WARNING, MESSAGE));
+    handler.publish(newLogRecord(Level.FINEST, MESSAGE));
+    handler.publish(newLogRecord(Level.FINER, MESSAGE));
+    handler.publish(newLogRecord(Level.FINE, MESSAGE));
+    handler.publish(newLogRecord(Level.CONFIG, MESSAGE));
+    handler.publish(newLogRecord(Level.INFO, MESSAGE));
+    handler.publish(newLogRecord(Level.WARNING, MESSAGE));
   }
 
   @Test
@@ -282,12 +302,12 @@ public void testFlushLevel() {
     handler.setFlushSize(100);
     handler.setFlushLevel(Level.WARNING);
     handler.setFormatter(new TestFormatter());
-    handler.publish(new LogRecord(Level.FINEST, MESSAGE));
-    handler.publish(new LogRecord(Level.FINER, MESSAGE));
-    handler.publish(new LogRecord(Level.FINE, MESSAGE));
-    handler.publish(new LogRecord(Level.CONFIG, MESSAGE));
-    handler.publish(new LogRecord(Level.INFO, MESSAGE));
-    handler.publish(new LogRecord(Level.WARNING, MESSAGE));
+    handler.publish(newLogRecord(Level.FINEST, MESSAGE));
+    handler.publish(newLogRecord(Level.FINER, MESSAGE));
+    handler.publish(newLogRecord(Level.FINE, MESSAGE));
+    handler.publish(newLogRecord(Level.CONFIG, MESSAGE));
+    handler.publish(newLogRecord(Level.INFO, MESSAGE));
+    handler.publish(newLogRecord(Level.WARNING, MESSAGE));
   }
 
   @Test
@@ -298,13 +318,18 @@ public void testAddHandler() {
         WriteOption.resource(DEFAULT_RESOURCE));
     EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null));
     EasyMock.replay(options, logging);
-    LoggingHandler handler = new LoggingHandler(LOG_NAME, options);
+    LoggingHandler handler = new LoggingHandler(LOG_NAME, options) {
+      @Override
+      public void close() {
+        // Make close NOOP to avoid mock close exception
+      }
+    };
     handler.setLevel(Level.ALL);
     handler.setFormatter(new TestFormatter());
     Logger logger = Logger.getLogger(getClass().getName());
     logger.setLevel(Level.ALL);
     LoggingHandler.addHandler(logger, handler);
-    logger.finest(MESSAGE);
+    logger.log(newLogRecord(Level.FINEST, MESSAGE));
   }
 
   @Test
@@ -320,7 +345,7 @@ public void testClose() throws Exception {
     Handler handler = new LoggingHandler(LOG_NAME, options);
     handler.setLevel(Level.ALL);
     handler.setFormatter(new TestFormatter());
-    handler.publish(new LogRecord(Level.FINEST, MESSAGE));
+    handler.publish(newLogRecord(Level.FINEST, MESSAGE));
     handler.close();
     handler.close();
   }

From 34c186fa7a9d86b0e58284ce542db5a7806190b7 Mon Sep 17 00:00:00 2001
From: Greg Wilkins 
Date: Thu, 19 Jan 2017 15:09:11 +1100
Subject: [PATCH 14/22] Initialize the default MonitoredResource from a GAE
 environment (#1535)

---
 .../cloud/logging/GaeFlexLoggingEnhancer.java | 88 +++++++++++++++++++
 .../google/cloud/logging/LoggingHandler.java  | 80 +++++++++++++++--
 .../cloud/logging/LoggingHandlerTest.java     | 37 ++++++++
 3 files changed, 199 insertions(+), 6 deletions(-)
 create mode 100644 google-cloud-logging/src/main/java/com/google/cloud/logging/GaeFlexLoggingEnhancer.java

diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/GaeFlexLoggingEnhancer.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/GaeFlexLoggingEnhancer.java
new file mode 100644
index 000000000000..b4c80e288007
--- /dev/null
+++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/GaeFlexLoggingEnhancer.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.logging;
+
+import java.util.logging.LogRecord;
+
+import com.google.cloud.MonitoredResource.Builder;
+
+/**
+ * A Logging {@link Enhancer} that enhances the logging for the 
+ * GAE Flex environment. This enhancer can
+ * be configured in a logging.properties file with:
+ * 
+ * 
+ * handlers=com.google.cloud.logging.LoggingHandler
+ * com.google.cloud.logging.LoggingHandler.log=gaeflex.log
+ * com.google.cloud.logging.LoggingHandler.resourceType=gae_app
+ * com.google.cloud.logging.LoggingHandler.enhancers=com.google.cloud.logging.GaeFlexLoggingEnhancer
+ * com.google.cloud.logging.LoggingHandler.formatter = java.util.logging.SimpleFormatter
+ * java.util.logging.SimpleFormatter.format=%3$s: %5$s%6$s
+ * 
+ * + */ +public class GaeFlexLoggingEnhancer implements LoggingHandler.Enhancer { + + private static final ThreadLocal traceId = new ThreadLocal<>(); + + private final String gaeInstanceId; + + /** + * Set the Trace ID associated with any logging done by the current thread. + * + * @param id The traceID + */ + public static void setCurrentTraceId(String id) { + traceId.set(id); + } + + /** + * Get the Trace ID associated with any logging done by the current thread. + * + * @return id The traceID + */ + public static String getCurrentTraceId() { + return traceId.get(); + } + + public GaeFlexLoggingEnhancer() { + gaeInstanceId = System.getenv("GAE_INSTANCE"); // Are we running on a GAE instance? + } + + @Override + public void enhanceMonitoredResource(Builder builder) { + if (gaeInstanceId != null) { + if (System.getenv("GAE_SERVICE") != null) { + builder.addLabel("module_id", System.getenv("GAE_SERVICE")); + } + if (System.getenv("GAE_VERSION") != null) { + builder.addLabel("version_id", System.getenv("GAE_VERSION")); + } + } + } + + @Override + public void enhanceLogEntry(com.google.cloud.logging.LogEntry.Builder builder, LogRecord record) { + if (gaeInstanceId != null) { + builder.addLabel("appengine.googleapis.com/instance_name", gaeInstanceId); + } + String traceId = getCurrentTraceId(); + if (traceId != null) { + builder.addLabel("appengine.googleapis.com/trace_id", traceId); + } + } +} diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java index d497f6e1b70b..ecb2706d9528 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java @@ -21,7 +21,9 @@ import com.google.cloud.MonitoredResource; import com.google.cloud.logging.Logging.WriteOption; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; + +import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.logging.ErrorManager; @@ -76,6 +78,11 @@ *
  • {@code com.google.cloud.logging.LoggingHandler.flushLevel} specifies the flush log level. * When a log with this level is published, logs are transmitted to the Stackdriver Logging * service (defaults to {@link LoggingLevel#ERROR}). + *
  • {@code com.google.cloud.logging.LoggingHandler.enhancers} specifies a comma separated list + * of {@link Enhancer} classes. This handler will call each enhancer list whenever it builds + * a {@link MonitoredResource} or {@link LogEntry} instance (defaults to empty list). + *
  • {@code com.google.cloud.logging.LoggingHandler.resourceType} the type name to use when + * creating the default {@link MonitoredResource} (defaults to "global"). * * *

    To add a {@code LoggingHandler} to an existing {@link Logger} and be sure to avoid infinite @@ -99,6 +106,7 @@ public class LoggingHandler extends Handler { private volatile Logging logging; private Level flushLevel; private long flushSize; + private final List enhancers; /** * Creates an handler that publishes messages to Stackdriver Logging. @@ -131,9 +139,30 @@ public LoggingHandler(String log, LoggingOptions options) { * * @param log the name of the log to which log entries are written * @param options options for the Stackdriver Logging service - * @param monitoredResource the monitored resource to which log entries refer + * @param monitoredResource the monitored resource to which log entries refer. If it is null + * then a default resource is created based on the project ID. When creating a default resource, if + * any {@link Enhancer} instances are configured and then each + * {@link Enhancer#enhanceMonitoredResource(com.google.cloud.MonitoredResource.Builder)} method + * is called before building the default resource. */ public LoggingHandler(String log, LoggingOptions options, MonitoredResource monitoredResource) { + this(log, options, monitoredResource,null); + } + + /** + * Creates a handler that publishes messages to Stackdriver Logging. + * + * @param log the name of the log to which log entries are written + * @param options options for the Stackdriver Logging service + * @param monitoredResource the monitored resource to which log entries refer. If it is null + * then a default resource is created based on the project ID. When creating a default resource, if + * any {@link Enhancer} instances are configured and then each + * {@link Enhancer#enhanceMonitoredResource(com.google.cloud.MonitoredResource.Builder)} method + * is called before building the default resource. + * @param enhancers List of {@link Enhancer} instances used to enhance any {@link MonitoredResource} + * or {@link LogEntry} instances built by this handler. + */ + public LoggingHandler(String log, LoggingOptions options, MonitoredResource monitoredResource, List enhancers) { LogConfigHelper helper = new LogConfigHelper(); String className = getClass().getName(); this.options = options != null ? options : LoggingOptions.getDefaultInstance(); @@ -143,7 +172,9 @@ public LoggingHandler(String log, LoggingOptions options, MonitoredResource moni setFilter(helper.getFilterProperty(className + ".filter", null)); setFormatter(helper.getFormatterProperty(className + ".formatter", new SimpleFormatter())); String logName = firstNonNull(log, helper.getProperty(className + ".log", "java.log")); - MonitoredResource resource = firstNonNull(monitoredResource, getDefaultResource()); + this.enhancers = enhancers != null ? enhancers : helper.getEnhancerProperty(className + ".enhancers"); + String resourceType = helper.getProperty(className + ".resourceType", "global"); + MonitoredResource resource = monitoredResource != null ? monitoredResource : getDefaultResource(resourceType); writeOptions = new WriteOption[]{WriteOption.logName(logName), WriteOption.resource(resource)}; } @@ -178,8 +209,13 @@ private static boolean hasLoggingHandler(Logger logger) { return false; } - private MonitoredResource getDefaultResource() { - return MonitoredResource.of("global", ImmutableMap.of("project_id", options.getProjectId())); + private MonitoredResource getDefaultResource(String resourceType) { + MonitoredResource.Builder builder = MonitoredResource.newBuilder(resourceType); + builder.addLabel("project_id", options.getProjectId()); + for (Enhancer enhancer : enhancers) { + enhancer.enhanceMonitoredResource(builder); + } + return builder.build(); } private static class LogConfigHelper { @@ -237,6 +273,24 @@ Formatter getFormatterProperty(String name, Formatter defaultValue) { } return defaultValue; } + + List getEnhancerProperty(String name) { + String list = manager.getProperty(name); + try { + List enhancers = new ArrayList<>(); + if (list != null) { + String[] items = list.split(","); + for (String e_name : items) { + Class clz = (Class) ClassLoader.getSystemClassLoader().loadClass(e_name); + enhancers.add((Enhancer) clz.newInstance()); + } + } + return enhancers; + } catch (Exception ex) { + // If we cannot create the enhancers we fall back to the default + } + return Collections.emptyList(); + } } /** @@ -311,12 +365,16 @@ private LogEntry entryFor(LogRecord record) { .addLabel("levelValue", String.valueOf(level.intValue())) .setTimestamp(record.getMillis()) .setSeverity(severityFor(level)); + + for (Enhancer enhancer : enhancers) { + enhancer.enhanceLogEntry(builder, record); + } enhanceLogEntry(builder, record); return builder.build(); } + @Deprecated protected void enhanceLogEntry(LogEntry.Builder builder, LogRecord record) { - // no-op in this class } private static Severity severityFor(Level level) { @@ -430,4 +488,14 @@ public synchronized long setFlushSize(long flushSize) { public static void addHandler(Logger logger, LoggingHandler handler) { logger.addHandler(handler); } + + /** + * A Log Enhancer. + * May be used to enhance the {@link MonitoredResource} and/or the {@link LogEntry} + */ + interface Enhancer { + void enhanceMonitoredResource(MonitoredResource.Builder builder); + void enhanceLogEntry(LogEntry.Builder builder, LogRecord record); + } + } diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java index a1c1697d18af..7b4f3298c1d4 100644 --- a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java +++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java @@ -17,11 +17,14 @@ package com.google.cloud.logging; import com.google.cloud.MonitoredResource; +import com.google.cloud.logging.LogEntry.Builder; import com.google.cloud.logging.Logging.WriteOption; import com.google.cloud.logging.Payload.StringPayload; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.Futures; + +import java.util.Collections; import java.util.logging.ErrorManager; import java.util.logging.Formatter; import java.util.logging.Handler; @@ -46,6 +49,13 @@ public class LoggingHandlerTest { .addLabel("levelValue", String.valueOf(Level.FINEST.intValue())) .setTimestamp(123456789L) .build(); + private static final LogEntry FINEST_ENHANCED_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE)) + .setSeverity(Severity.DEBUG) + .addLabel("levelName", "FINEST") + .addLabel("levelValue", String.valueOf(Level.FINEST.intValue())) + .addLabel("enhanced", "true") + .setTimestamp(123456789L) + .build(); private static final LogEntry FINER_ENTRY = LogEntry.newBuilder(StringPayload.of(MESSAGE)) .setSeverity(Severity.DEBUG) .addLabel("levelName", "FINER") @@ -227,6 +237,33 @@ public void testPublishCustomResource() { handler.publish(newLogRecord(Level.FINEST, MESSAGE)); } + @Test + public void testEnhancedLogEntry() { + EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes(); + EasyMock.expect(options.getService()).andReturn(logging); + MonitoredResource resource = MonitoredResource.of("custom", ImmutableMap.of()); + logging.writeAsync(ImmutableList.of(FINEST_ENHANCED_ENTRY), WriteOption.logName(LOG_NAME), + WriteOption.resource(resource)); + EasyMock.expectLastCall().andReturn(Futures.immediateFuture(null)); + EasyMock.replay(options, logging); + LoggingHandler.Enhancer enhancer = new LoggingHandler.Enhancer() { + @Override + public void enhanceMonitoredResource(MonitoredResource.Builder builder) { + throw new IllegalStateException(); + } + + @Override + public void enhanceLogEntry(Builder builder, LogRecord record) { + builder.addLabel("enhanced", "true"); + } + }; + Handler handler = + new LoggingHandler(LOG_NAME, options, resource, Collections.singletonList(enhancer)); + handler.setLevel(Level.ALL); + handler.setFormatter(new TestFormatter()); + handler.publish(newLogRecord(Level.FINEST, MESSAGE)); + } + @Test public void testReportFlushError() { EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes(); From 7b6e5c65d2c3dd31fab465ced97b1e30ce5e6cab Mon Sep 17 00:00:00 2001 From: vam Date: Fri, 27 Jan 2017 14:15:30 -0800 Subject: [PATCH 15/22] BigQuery: Add support to FormatOptions for AVRO https://github.com/GoogleCloudPlatform/google-cloud-java/issues/1441 Added new constant in FormatOptions, and a corresponding factory method. Updated test cases. Confirmed that AVRO does not require special treatment (like CSV does), so no additional changes are required. --- .../java/com/google/cloud/bigquery/FormatOptions.java | 8 ++++++++ .../java/com/google/cloud/bigquery/FormatOptionsTest.java | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java index ef451e844a3e..40ec11ebf6b9 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java @@ -32,6 +32,7 @@ public class FormatOptions implements Serializable { static final String CSV = "CSV"; static final String JSON = "NEWLINE_DELIMITED_JSON"; static final String DATASTORE_BACKUP = "DATASTORE_BACKUP"; + static final String AVRO = "AVRO"; private static final long serialVersionUID = -443376052020423691L; private final String type; @@ -94,6 +95,13 @@ public static FormatOptions datastoreBackup() { return new FormatOptions(DATASTORE_BACKUP); } + /** + * Default options for AVRO format. + */ + public static FormatOptions avro() { + return new FormatOptions(AVRO); + } + /** * Default options for the provided format. */ diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java index bd231f0249e6..a019e347ba84 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java @@ -30,9 +30,12 @@ public void testConstructor() { assertEquals(FormatOptions.JSON, options.getType()); options = new FormatOptions(FormatOptions.DATASTORE_BACKUP); assertEquals(FormatOptions.DATASTORE_BACKUP, options.getType()); + options = new FormatOptions(FormatOptions.AVRO); + assertEquals(FormatOptions.AVRO, options.getType()); } @Test + @SuppressWarnings("deprecation") public void testConstructorDeprecated() { FormatOptions options = new FormatOptions(FormatOptions.CSV); assertEquals(FormatOptions.CSV, options.type()); @@ -40,6 +43,8 @@ public void testConstructorDeprecated() { assertEquals(FormatOptions.JSON, options.type()); options = new FormatOptions(FormatOptions.DATASTORE_BACKUP); assertEquals(FormatOptions.DATASTORE_BACKUP, options.type()); + options = new FormatOptions(FormatOptions.AVRO); + assertEquals(FormatOptions.AVRO, options.type()); } @Test @@ -47,6 +52,7 @@ public void testFactoryMethods() { assertEquals(FormatOptions.CSV, FormatOptions.csv().getType()); assertEquals(FormatOptions.JSON, FormatOptions.json().getType()); assertEquals(FormatOptions.DATASTORE_BACKUP, FormatOptions.datastoreBackup().getType()); + assertEquals(FormatOptions.AVRO, FormatOptions.avro().getType()); } @Test From 6d193de0bd228543ba287aae953bee907fca5785 Mon Sep 17 00:00:00 2001 From: vam Date: Fri, 27 Jan 2017 14:22:09 -0800 Subject: [PATCH 16/22] Reverting changed commited by mistake before review. --- .../java/com/google/cloud/bigquery/FormatOptions.java | 8 -------- .../java/com/google/cloud/bigquery/FormatOptionsTest.java | 6 ------ 2 files changed, 14 deletions(-) diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java index 40ec11ebf6b9..ef451e844a3e 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java @@ -32,7 +32,6 @@ public class FormatOptions implements Serializable { static final String CSV = "CSV"; static final String JSON = "NEWLINE_DELIMITED_JSON"; static final String DATASTORE_BACKUP = "DATASTORE_BACKUP"; - static final String AVRO = "AVRO"; private static final long serialVersionUID = -443376052020423691L; private final String type; @@ -95,13 +94,6 @@ public static FormatOptions datastoreBackup() { return new FormatOptions(DATASTORE_BACKUP); } - /** - * Default options for AVRO format. - */ - public static FormatOptions avro() { - return new FormatOptions(AVRO); - } - /** * Default options for the provided format. */ diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java index a019e347ba84..bd231f0249e6 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java @@ -30,12 +30,9 @@ public void testConstructor() { assertEquals(FormatOptions.JSON, options.getType()); options = new FormatOptions(FormatOptions.DATASTORE_BACKUP); assertEquals(FormatOptions.DATASTORE_BACKUP, options.getType()); - options = new FormatOptions(FormatOptions.AVRO); - assertEquals(FormatOptions.AVRO, options.getType()); } @Test - @SuppressWarnings("deprecation") public void testConstructorDeprecated() { FormatOptions options = new FormatOptions(FormatOptions.CSV); assertEquals(FormatOptions.CSV, options.type()); @@ -43,8 +40,6 @@ public void testConstructorDeprecated() { assertEquals(FormatOptions.JSON, options.type()); options = new FormatOptions(FormatOptions.DATASTORE_BACKUP); assertEquals(FormatOptions.DATASTORE_BACKUP, options.type()); - options = new FormatOptions(FormatOptions.AVRO); - assertEquals(FormatOptions.AVRO, options.type()); } @Test @@ -52,7 +47,6 @@ public void testFactoryMethods() { assertEquals(FormatOptions.CSV, FormatOptions.csv().getType()); assertEquals(FormatOptions.JSON, FormatOptions.json().getType()); assertEquals(FormatOptions.DATASTORE_BACKUP, FormatOptions.datastoreBackup().getType()); - assertEquals(FormatOptions.AVRO, FormatOptions.avro().getType()); } @Test From 1643cf19cb88dc656c0c32ed4def29212b2f59b4 Mon Sep 17 00:00:00 2001 From: vam Date: Fri, 27 Jan 2017 14:26:17 -0800 Subject: [PATCH 17/22] BigQuery: Add support to FormatOptions for AVRO https://github.com/GoogleCloudPlatform/google-cloud-java/issues/1441 Added new constant in FormatOptions and a corresponding factory method. Updated test cases. Confirmed that AVRO does not require special treatment (like CSV does), so no additional changes are required. --- .../java/com/google/cloud/bigquery/FormatOptions.java | 8 ++++++++ .../java/com/google/cloud/bigquery/FormatOptionsTest.java | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java index ef451e844a3e..40ec11ebf6b9 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java @@ -32,6 +32,7 @@ public class FormatOptions implements Serializable { static final String CSV = "CSV"; static final String JSON = "NEWLINE_DELIMITED_JSON"; static final String DATASTORE_BACKUP = "DATASTORE_BACKUP"; + static final String AVRO = "AVRO"; private static final long serialVersionUID = -443376052020423691L; private final String type; @@ -94,6 +95,13 @@ public static FormatOptions datastoreBackup() { return new FormatOptions(DATASTORE_BACKUP); } + /** + * Default options for AVRO format. + */ + public static FormatOptions avro() { + return new FormatOptions(AVRO); + } + /** * Default options for the provided format. */ diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java index bd231f0249e6..a019e347ba84 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java @@ -30,9 +30,12 @@ public void testConstructor() { assertEquals(FormatOptions.JSON, options.getType()); options = new FormatOptions(FormatOptions.DATASTORE_BACKUP); assertEquals(FormatOptions.DATASTORE_BACKUP, options.getType()); + options = new FormatOptions(FormatOptions.AVRO); + assertEquals(FormatOptions.AVRO, options.getType()); } @Test + @SuppressWarnings("deprecation") public void testConstructorDeprecated() { FormatOptions options = new FormatOptions(FormatOptions.CSV); assertEquals(FormatOptions.CSV, options.type()); @@ -40,6 +43,8 @@ public void testConstructorDeprecated() { assertEquals(FormatOptions.JSON, options.type()); options = new FormatOptions(FormatOptions.DATASTORE_BACKUP); assertEquals(FormatOptions.DATASTORE_BACKUP, options.type()); + options = new FormatOptions(FormatOptions.AVRO); + assertEquals(FormatOptions.AVRO, options.type()); } @Test @@ -47,6 +52,7 @@ public void testFactoryMethods() { assertEquals(FormatOptions.CSV, FormatOptions.csv().getType()); assertEquals(FormatOptions.JSON, FormatOptions.json().getType()); assertEquals(FormatOptions.DATASTORE_BACKUP, FormatOptions.datastoreBackup().getType()); + assertEquals(FormatOptions.AVRO, FormatOptions.avro().getType()); } @Test From cc6796a1982f7f45a194653fff9af0a072bfcd2b Mon Sep 17 00:00:00 2001 From: Michael Darakananda Date: Mon, 30 Jan 2017 21:15:36 +1100 Subject: [PATCH 18/22] use RpcFuture and remove old BundlingSettings (#1572) * use RpcFuture and remove old BundlingSettings --- google-cloud-core/pom.xml | 2 +- .../com/google/cloud/logging/LoggingImpl.java | 4 -- .../cloud/logging/spi/DefaultLoggingRpc.java | 58 ++++++++-------- .../spi/v2/LoggingServiceV2Settings.java | 5 +- .../com/google/cloud/pubsub/PubSubImpl.java | 4 -- .../cloud/pubsub/spi/DefaultPubSubRpc.java | 67 +++++++++---------- .../google/cloud/pubsub/spi/PubSubRpc.java | 1 - .../pubsub/spi/v1/PublisherSettings.java | 5 +- 8 files changed, 64 insertions(+), 82 deletions(-) diff --git a/google-cloud-core/pom.xml b/google-cloud-core/pom.xml index 34974a496520..47f215f24233 100644 --- a/google-cloud-core/pom.xml +++ b/google-cloud-core/pom.xml @@ -111,7 +111,7 @@ com.google.api gax - 0.0.25 + 0.0.28 io.grpc diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java index 47dfc175d403..b7bf7161a04f 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java @@ -40,7 +40,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.Uninterruptibles; import com.google.logging.v2.CreateLogMetricRequest; import com.google.logging.v2.CreateSinkRequest; @@ -107,9 +106,6 @@ private static V get(Future future) { private static Future transform(Future future, Function function) { - if (future instanceof ListenableFuture) { - return Futures.transform((ListenableFuture) future, function); - } return Futures.lazyTransform(future, function); } diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/DefaultLoggingRpc.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/DefaultLoggingRpc.java index dd1ed10f40f2..1673151a315b 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/DefaultLoggingRpc.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/DefaultLoggingRpc.java @@ -16,6 +16,8 @@ package com.google.cloud.logging.spi; +import com.google.api.gax.core.Function; +import com.google.api.gax.core.RpcFuture; import com.google.api.gax.grpc.ApiException; import com.google.api.gax.grpc.ChannelProvider; import com.google.api.gax.grpc.ExecutorProvider; @@ -33,10 +35,6 @@ import com.google.cloud.logging.spi.v2.LoggingServiceV2Settings; import com.google.cloud.logging.spi.v2.MetricsServiceV2Client; import com.google.cloud.logging.spi.v2.MetricsServiceV2Settings; -import com.google.common.base.Function; -import com.google.common.collect.Sets; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import com.google.logging.v2.CreateLogMetricRequest; import com.google.logging.v2.CreateSinkRequest; import com.google.logging.v2.DeleteLogMetricRequest; @@ -59,13 +57,13 @@ import com.google.logging.v2.WriteLogEntriesRequest; import com.google.logging.v2.WriteLogEntriesResponse; import com.google.protobuf.Empty; - import io.grpc.ManagedChannel; import io.grpc.Status.Code; import io.grpc.netty.NegotiationType; import io.grpc.netty.NettyChannelBuilder; - import java.io.IOException; +import java.util.Collections; +import java.util.EnumSet; import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; @@ -148,21 +146,25 @@ public DefaultLoggingRpc(LoggingOptions options) throws IOException { } } - private static Future translate(ListenableFuture from, final boolean idempotent, - int... returnNullOn) { - final Set returnNullOnSet = Sets.newHashSetWithExpectedSize(returnNullOn.length); - for (int value : returnNullOn) { - returnNullOnSet.add(value); + private static Future translate( + RpcFuture from, final boolean idempotent, Code... returnNullOn) { + final Set returnNullOnSet; + if (returnNullOn.length > 0) { + returnNullOnSet = EnumSet.of(returnNullOn[0], returnNullOn); + } else { + returnNullOnSet = Collections.emptySet(); } - return Futures.catching(from, ApiException.class, new Function() { - @Override - public V apply(ApiException exception) { - if (returnNullOnSet.contains(exception.getStatusCode().value())) { - return null; - } - throw new LoggingException(exception, idempotent); - } - }); + return from.catching( + ApiException.class, + new Function() { + @Override + public V apply(ApiException exception) { + if (returnNullOnSet.contains(exception.getStatusCode().value())) { + return null; + } + throw new LoggingException(exception, idempotent); + } + }); } @Override @@ -177,7 +179,7 @@ public Future update(UpdateSinkRequest request) { @Override public Future get(GetSinkRequest request) { - return translate(configClient.getSinkCallable().futureCall(request), true, Code.NOT_FOUND.value()); + return translate(configClient.getSinkCallable().futureCall(request), true, Code.NOT_FOUND); } @Override @@ -187,14 +189,12 @@ public Future list(ListSinksRequest request) { @Override public Future delete(DeleteSinkRequest request) { - return translate(configClient.deleteSinkCallable().futureCall(request), true, - Code.NOT_FOUND.value()); + return translate(configClient.deleteSinkCallable().futureCall(request), true, Code.NOT_FOUND); } @Override public Future delete(DeleteLogRequest request) { - return translate(loggingClient.deleteLogCallable().futureCall(request), true, - Code.NOT_FOUND.value()); + return translate(loggingClient.deleteLogCallable().futureCall(request), true, Code.NOT_FOUND); } @Override @@ -226,8 +226,8 @@ public Future update(UpdateLogMetricRequest request) { @Override public Future get(GetLogMetricRequest request) { - return translate(metricsClient.getLogMetricCallable().futureCall(request), true, - Code.NOT_FOUND.value()); + return translate( + metricsClient.getLogMetricCallable().futureCall(request), true, Code.NOT_FOUND); } @Override @@ -237,8 +237,8 @@ public Future list(ListLogMetricsRequest request) { @Override public Future delete(DeleteLogMetricRequest request) { - return translate(metricsClient.deleteLogMetricCallable().futureCall(request), true, - Code.NOT_FOUND.value()); + return translate( + metricsClient.deleteLogMetricCallable().futureCall(request), true, Code.NOT_FOUND); } @Override diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Settings.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Settings.java index 9ea30ed25120..d4803273cf43 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Settings.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/spi/v2/LoggingServiceV2Settings.java @@ -546,11 +546,8 @@ private static Builder createDefault() { .writeLogEntriesSettings() .getBundlingSettingsBuilder() .setElementCountThreshold(1) - .setElementCountLimit(1000) .setRequestByteThreshold(1024) - .setRequestByteLimit(10485760) - .setDelayThreshold(Duration.millis(10)) - .setBlockingCallCountThreshold(1); + .setDelayThreshold(Duration.millis(10)); builder .writeLogEntriesSettings() .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("non_idempotent")) diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java index 35dff5ab2524..e67aee158979 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java @@ -41,7 +41,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.Uninterruptibles; import com.google.iam.v1.SetIamPolicyRequest; import com.google.iam.v1.TestIamPermissionsRequest; @@ -222,9 +221,6 @@ private static V get(Future future) { private static Future transform(Future future, Function function) { - if (future instanceof ListenableFuture) { - return Futures.transform((ListenableFuture) future, function); - } return Futures.lazyTransform(future, function); } diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java index 390b16326ef8..4af17e458aeb 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java @@ -16,6 +16,10 @@ package com.google.cloud.pubsub.spi; +import com.google.api.gax.core.ForwardingRpcFuture; +import com.google.api.gax.core.Function; +import com.google.api.gax.core.RpcFuture; +import com.google.api.gax.core.RpcFutureCallback; import com.google.api.gax.grpc.ApiException; import com.google.api.gax.grpc.ChannelProvider; import com.google.api.gax.grpc.ExecutorProvider; @@ -31,12 +35,7 @@ import com.google.cloud.pubsub.spi.v1.PublisherSettings; import com.google.cloud.pubsub.spi.v1.SubscriberClient; import com.google.cloud.pubsub.spi.v1.SubscriberSettings; -import com.google.common.base.Function; import com.google.common.collect.Sets; -import com.google.common.util.concurrent.ForwardingListenableFuture; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import com.google.iam.v1.GetIamPolicyRequest; import com.google.iam.v1.Policy; import com.google.iam.v1.SetIamPolicyRequest; @@ -62,18 +61,15 @@ import com.google.pubsub.v1.PullResponse; import com.google.pubsub.v1.Subscription; import com.google.pubsub.v1.Topic; - import io.grpc.ManagedChannel; import io.grpc.Status.Code; import io.grpc.netty.NegotiationType; import io.grpc.netty.NettyChannelBuilder; - -import org.joda.time.Duration; - import java.io.IOException; import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; +import org.joda.time.Duration; public class DefaultPubSubRpc implements PubSubRpc { @@ -110,27 +106,26 @@ protected ChannelProvider getChannelProvider() { } } - private static final class PullFutureImpl - extends ForwardingListenableFuture.SimpleForwardingListenableFuture + private static final class PullFutureImpl extends ForwardingRpcFuture implements PullFuture { - - PullFutureImpl(ListenableFuture delegate) { + PullFutureImpl(RpcFuture delegate) { super(delegate); } @Override public void addCallback(final PullCallback callback) { - Futures.addCallback(delegate(), new FutureCallback() { - @Override - public void onSuccess(PullResponse result) { - callback.success(result); - } - - @Override - public void onFailure(Throwable error) { - callback.failure(error); - } - }); + addCallback( + new RpcFutureCallback() { + @Override + public void onSuccess(PullResponse response) { + callback.success(response); + } + + @Override + public void onFailure(Throwable error) { + callback.failure(error); + } + }); } } @@ -178,21 +173,23 @@ public DefaultPubSubRpc(PubSubOptions options) throws IOException { } } - private static ListenableFuture translate(ListenableFuture from, - final boolean idempotent, int... returnNullOn) { + private static RpcFuture translate( + RpcFuture from, final boolean idempotent, int... returnNullOn) { final Set returnNullOnSet = Sets.newHashSetWithExpectedSize(returnNullOn.length); for (int value : returnNullOn) { returnNullOnSet.add(value); } - return Futures.catching(from, ApiException.class, new Function() { - @Override - public V apply(ApiException exception) { - if (returnNullOnSet.contains(exception.getStatusCode().value())) { - return null; - } - throw new PubSubException(exception, idempotent); - } - }); + return from.catching( + ApiException.class, + new Function() { + @Override + public V apply(ApiException exception) { + if (returnNullOnSet.contains(exception.getStatusCode().value())) { + return null; + } + throw new PubSubException(exception, idempotent); + } + }); } @Override diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpc.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpc.java index 172f2e8dcc5f..30a5688b01c5 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpc.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpc.java @@ -40,7 +40,6 @@ import com.google.pubsub.v1.PullResponse; import com.google.pubsub.v1.Subscription; import com.google.pubsub.v1.Topic; - import java.util.concurrent.Future; public interface PubSubRpc extends AutoCloseable { diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PublisherSettings.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PublisherSettings.java index 72e0d0128d62..db1a202392ab 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PublisherSettings.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PublisherSettings.java @@ -527,11 +527,8 @@ private static Builder createDefault() { .publishSettings() .getBundlingSettingsBuilder() .setElementCountThreshold(10) - .setElementCountLimit(1000) .setRequestByteThreshold(1024) - .setRequestByteLimit(10485760) - .setDelayThreshold(Duration.millis(10)) - .setBlockingCallCountThreshold(1); + .setDelayThreshold(Duration.millis(10)); builder .publishSettings() .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("one_plus_delivery")) From e54f2faa8260fe0cb2b7ef813d942132b68f86a4 Mon Sep 17 00:00:00 2001 From: Garrett Jones Date: Wed, 1 Feb 2017 11:07:51 -0800 Subject: [PATCH 19/22] Release 0.8.2 Note: This version was accidentally released to Sonatype because of experimenting with deployment commands, combined with the fact that autoReleaseAfterClose is set to true. Since releases can't be taken back, we might as well own up to the release and push the code forward. --- google-cloud-bigquery/pom.xml | 2 +- google-cloud-compute/pom.xml | 2 +- google-cloud-contrib/google-cloud-nio-examples/pom.xml | 2 +- google-cloud-contrib/google-cloud-nio/pom.xml | 2 +- google-cloud-contrib/pom.xml | 2 +- google-cloud-core/pom.xml | 2 +- google-cloud-datastore/pom.xml | 2 +- google-cloud-dns/pom.xml | 2 +- google-cloud-errorreporting/pom.xml | 2 +- google-cloud-examples/pom.xml | 2 +- google-cloud-language/pom.xml | 2 +- google-cloud-logging/pom.xml | 2 +- google-cloud-monitoring/pom.xml | 2 +- google-cloud-pubsub/pom.xml | 2 +- google-cloud-resourcemanager/pom.xml | 2 +- google-cloud-speech/pom.xml | 2 +- google-cloud-storage/pom.xml | 2 +- google-cloud-trace/pom.xml | 2 +- google-cloud-translate/pom.xml | 2 +- google-cloud-vision/pom.xml | 2 +- google-cloud/pom.xml | 2 +- pom.xml | 6 +++--- 22 files changed, 24 insertions(+), 24 deletions(-) diff --git a/google-cloud-bigquery/pom.xml b/google-cloud-bigquery/pom.xml index 46ce9486a257..b433bc49fe16 100644 --- a/google-cloud-bigquery/pom.xml +++ b/google-cloud-bigquery/pom.xml @@ -12,7 +12,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-bigquery diff --git a/google-cloud-compute/pom.xml b/google-cloud-compute/pom.xml index 15910c065623..b4e9ffcc4aad 100644 --- a/google-cloud-compute/pom.xml +++ b/google-cloud-compute/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-compute diff --git a/google-cloud-contrib/google-cloud-nio-examples/pom.xml b/google-cloud-contrib/google-cloud-nio-examples/pom.xml index e3cbb6da9ed3..9822164147c2 100644 --- a/google-cloud-contrib/google-cloud-nio-examples/pom.xml +++ b/google-cloud-contrib/google-cloud-nio-examples/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-contrib - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-nio-examples diff --git a/google-cloud-contrib/google-cloud-nio/pom.xml b/google-cloud-contrib/google-cloud-nio/pom.xml index 28a6c2409464..297938dd5932 100644 --- a/google-cloud-contrib/google-cloud-nio/pom.xml +++ b/google-cloud-contrib/google-cloud-nio/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-contrib - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-nio diff --git a/google-cloud-contrib/pom.xml b/google-cloud-contrib/pom.xml index 5fd17e4e0b74..de5a2dbfdbc2 100644 --- a/google-cloud-contrib/pom.xml +++ b/google-cloud-contrib/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-contrib diff --git a/google-cloud-core/pom.xml b/google-cloud-core/pom.xml index 47f215f24233..7ad0a8ae40d6 100644 --- a/google-cloud-core/pom.xml +++ b/google-cloud-core/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-core diff --git a/google-cloud-datastore/pom.xml b/google-cloud-datastore/pom.xml index eedb8abfa699..5622bea2c75c 100644 --- a/google-cloud-datastore/pom.xml +++ b/google-cloud-datastore/pom.xml @@ -12,7 +12,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-datastore diff --git a/google-cloud-dns/pom.xml b/google-cloud-dns/pom.xml index 9de45f6e6c5e..368d39fb466d 100644 --- a/google-cloud-dns/pom.xml +++ b/google-cloud-dns/pom.xml @@ -13,7 +13,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-dns diff --git a/google-cloud-errorreporting/pom.xml b/google-cloud-errorreporting/pom.xml index 025eec9f697d..3e8053a6cc7c 100644 --- a/google-cloud-errorreporting/pom.xml +++ b/google-cloud-errorreporting/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-errorreporting diff --git a/google-cloud-examples/pom.xml b/google-cloud-examples/pom.xml index f754dd82e0af..495d4e431cb2 100644 --- a/google-cloud-examples/pom.xml +++ b/google-cloud-examples/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-examples diff --git a/google-cloud-language/pom.xml b/google-cloud-language/pom.xml index 82466d147541..b3b03b3c7156 100644 --- a/google-cloud-language/pom.xml +++ b/google-cloud-language/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-language diff --git a/google-cloud-logging/pom.xml b/google-cloud-logging/pom.xml index 1fabe063ff87..727feb6b1399 100644 --- a/google-cloud-logging/pom.xml +++ b/google-cloud-logging/pom.xml @@ -12,7 +12,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-logging diff --git a/google-cloud-monitoring/pom.xml b/google-cloud-monitoring/pom.xml index d7513bc8791e..e4b6a969e85c 100644 --- a/google-cloud-monitoring/pom.xml +++ b/google-cloud-monitoring/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-monitoring diff --git a/google-cloud-pubsub/pom.xml b/google-cloud-pubsub/pom.xml index adcc405c0a83..66ffcfcee6b9 100644 --- a/google-cloud-pubsub/pom.xml +++ b/google-cloud-pubsub/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-pubsub diff --git a/google-cloud-resourcemanager/pom.xml b/google-cloud-resourcemanager/pom.xml index d2991adff40f..29e1625a3ee9 100644 --- a/google-cloud-resourcemanager/pom.xml +++ b/google-cloud-resourcemanager/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-resourcemanager diff --git a/google-cloud-speech/pom.xml b/google-cloud-speech/pom.xml index 363aa3b54715..4d69d86b70cc 100644 --- a/google-cloud-speech/pom.xml +++ b/google-cloud-speech/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-speech diff --git a/google-cloud-storage/pom.xml b/google-cloud-storage/pom.xml index 335a91cb7688..a4e030c60944 100644 --- a/google-cloud-storage/pom.xml +++ b/google-cloud-storage/pom.xml @@ -12,7 +12,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-storage diff --git a/google-cloud-trace/pom.xml b/google-cloud-trace/pom.xml index e67fbc27238e..954d9b201edb 100644 --- a/google-cloud-trace/pom.xml +++ b/google-cloud-trace/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-trace diff --git a/google-cloud-translate/pom.xml b/google-cloud-translate/pom.xml index 048036efdbee..aac78a6e1215 100644 --- a/google-cloud-translate/pom.xml +++ b/google-cloud-translate/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-translate diff --git a/google-cloud-vision/pom.xml b/google-cloud-vision/pom.xml index c62784a28104..11ed894b6a53 100644 --- a/google-cloud-vision/pom.xml +++ b/google-cloud-vision/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha google-cloud-vision diff --git a/google-cloud/pom.xml b/google-cloud/pom.xml index 2f1042fc38fb..42e13b2f6207 100644 --- a/google-cloud/pom.xml +++ b/google-cloud/pom.xml @@ -11,7 +11,7 @@ com.google.cloud google-cloud-pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha diff --git a/pom.xml b/pom.xml index f42ef8a306b6..e6538721c1f5 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-pom pom - 0.8.2-alpha-SNAPSHOT + 0.8.2-alpha Google Cloud https://github.com/GoogleCloudPlatform/google-cloud-java @@ -92,8 +92,8 @@ github 0.6.0 1.0.3 - 0.8.2-alpha-SNAPSHOT - 0.8.2-beta-SNAPSHOT + 0.8.2-alpha + 0.8.2-beta ${beta.version} google-cloud From a7dcdab329f630470e63b3c6763210e4d204037d Mon Sep 17 00:00:00 2001 From: Garrett Jones Date: Wed, 1 Feb 2017 11:42:59 -0800 Subject: [PATCH 20/22] Updating version in README files. [ci skip] --- README.md | 6 +++--- google-cloud-bigquery/README.md | 6 +++--- google-cloud-compute/README.md | 6 +++--- google-cloud-contrib/README.md | 6 +++--- google-cloud-contrib/google-cloud-nio-examples/README.md | 4 ++-- google-cloud-contrib/google-cloud-nio/README.md | 6 +++--- google-cloud-core/README.md | 6 +++--- google-cloud-datastore/README.md | 6 +++--- google-cloud-dns/README.md | 6 +++--- google-cloud-examples/README.md | 6 +++--- google-cloud-logging/README.md | 6 +++--- google-cloud-pubsub/README.md | 6 +++--- google-cloud-resourcemanager/README.md | 6 +++--- google-cloud-storage/README.md | 6 +++--- google-cloud-translate/README.md | 6 +++--- google-cloud/README.md | 6 +++--- 16 files changed, 47 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 66ef8190bd3e..2c9168c5ba14 100644 --- a/README.md +++ b/README.md @@ -43,16 +43,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud - 0.8.1-alpha + 0.8.2-alpha ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud:0.8.1-alpha' +compile 'com.google.cloud:google-cloud:0.8.2-alpha' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud" % "0.8.1-alpha" +libraryDependencies += "com.google.cloud" % "google-cloud" % "0.8.2-alpha" ``` Example Applications diff --git a/google-cloud-bigquery/README.md b/google-cloud-bigquery/README.md index 58fa641af081..ccf9f32d1285 100644 --- a/google-cloud-bigquery/README.md +++ b/google-cloud-bigquery/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-bigquery - 0.8.0-beta + 0.8.2-beta ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-bigquery:0.8.0-beta' +compile 'com.google.cloud:google-cloud-bigquery:0.8.2-beta' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-bigquery" % "0.8.0-beta" +libraryDependencies += "com.google.cloud" % "google-cloud-bigquery" % "0.8.2-beta" ``` Example Application diff --git a/google-cloud-compute/README.md b/google-cloud-compute/README.md index 5104cfc772a6..6ebb96c7ff37 100644 --- a/google-cloud-compute/README.md +++ b/google-cloud-compute/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-compute - 0.8.1-alpha + 0.8.2-alpha ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-compute:0.8.1-alpha' +compile 'com.google.cloud:google-cloud-compute:0.8.2-alpha' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-compute" % "0.8.1-alpha" +libraryDependencies += "com.google.cloud" % "google-cloud-compute" % "0.8.2-alpha" ``` Example Application diff --git a/google-cloud-contrib/README.md b/google-cloud-contrib/README.md index c03d0545be5b..398caf673cd2 100644 --- a/google-cloud-contrib/README.md +++ b/google-cloud-contrib/README.md @@ -25,16 +25,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-contrib - 0.8.1-alpha + 0.8.2-alpha ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-contrib:0.8.1-alpha' +compile 'com.google.cloud:google-cloud-contrib:0.8.2-alpha' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-contrib" % "0.8.1-alpha" +libraryDependencies += "com.google.cloud" % "google-cloud-contrib" % "0.8.2-alpha" ``` ### google-cloud-nio-examples diff --git a/google-cloud-contrib/google-cloud-nio-examples/README.md b/google-cloud-contrib/google-cloud-nio-examples/README.md index 93c191c8895e..bddf1586402d 100644 --- a/google-cloud-contrib/google-cloud-nio-examples/README.md +++ b/google-cloud-contrib/google-cloud-nio-examples/README.md @@ -22,12 +22,12 @@ To run this example: 4. Run the sample with: ``` - java -cp google-cloud-contrib/google-cloud-nio/target/google-cloud-nio-0.8.2-alpha-SNAPSHOT-shaded.jar:google-cloud-contrib/google-cloud-nio-examples/target/google-cloud-nio-examples-0.8.2-alpha-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems + java -cp google-cloud-contrib/google-cloud-nio/target/google-cloud-nio-0.8.3-alpha-SNAPSHOT-shaded.jar:google-cloud-contrib/google-cloud-nio-examples/target/google-cloud-nio-examples-0.8.3-alpha-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems ``` Notice that it lists Google Cloud Storage, which it wouldn't if you ran it without the NIO jar: ``` - java -cp google-cloud-contrib/google-cloud-nio-examples/target/google-cloud-nio-examples-0.8.2-alpha-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems + java -cp google-cloud-contrib/google-cloud-nio-examples/target/google-cloud-nio-examples-0.8.3-alpha-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems ``` The sample doesn't have anything about Google Cloud Storage in it. It gets that ability from the NIO diff --git a/google-cloud-contrib/google-cloud-nio/README.md b/google-cloud-contrib/google-cloud-nio/README.md index c16102a1f5c0..74ec767491ed 100644 --- a/google-cloud-contrib/google-cloud-nio/README.md +++ b/google-cloud-contrib/google-cloud-nio/README.md @@ -26,16 +26,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-nio - 0.8.1-alpha + 0.8.2-alpha ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-nio:0.8.1-alpha' +compile 'com.google.cloud:google-cloud-nio:0.8.2-alpha' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-nio" % "0.8.1-alpha" +libraryDependencies += "com.google.cloud" % "google-cloud-nio" % "0.8.2-alpha" ``` Example Applications diff --git a/google-cloud-core/README.md b/google-cloud-core/README.md index 2f5971b4557c..a82cc30ff47f 100644 --- a/google-cloud-core/README.md +++ b/google-cloud-core/README.md @@ -19,16 +19,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-core - 0.8.1-alpha + 0.8.2-alpha ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-core:0.8.1-alpha' +compile 'com.google.cloud:google-cloud-core:0.8.2-alpha' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-core" % "0.8.1-alpha" +libraryDependencies += "com.google.cloud" % "google-cloud-core" % "0.8.2-alpha" ``` Troubleshooting diff --git a/google-cloud-datastore/README.md b/google-cloud-datastore/README.md index db84b8db9ad1..a313c65fa4ba 100644 --- a/google-cloud-datastore/README.md +++ b/google-cloud-datastore/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-datastore - 0.8.0-beta + 0.8.2-beta ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-datastore:0.8.0-beta' +compile 'com.google.cloud:google-cloud-datastore:0.8.2-beta' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-datastore" % "0.8.0-beta" +libraryDependencies += "com.google.cloud" % "google-cloud-datastore" % "0.8.2-beta" ``` Example Application diff --git a/google-cloud-dns/README.md b/google-cloud-dns/README.md index bbc185bc1218..ee23f46ed354 100644 --- a/google-cloud-dns/README.md +++ b/google-cloud-dns/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-dns - 0.8.1-alpha + 0.8.2-alpha ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-dns:0.8.1-alpha' +compile 'com.google.cloud:google-cloud-dns:0.8.2-alpha' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-dns" % "0.8.1-alpha" +libraryDependencies += "com.google.cloud" % "google-cloud-dns" % "0.8.2-alpha" ``` Example Application diff --git a/google-cloud-examples/README.md b/google-cloud-examples/README.md index f9e6f2dac0b2..3deb256e9968 100644 --- a/google-cloud-examples/README.md +++ b/google-cloud-examples/README.md @@ -19,16 +19,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-examples - 0.8.1-alpha + 0.8.2-alpha ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-examples:0.8.1-alpha' +compile 'com.google.cloud:google-cloud-examples:0.8.2-alpha' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-examples" % "0.8.1-alpha" +libraryDependencies += "com.google.cloud" % "google-cloud-examples" % "0.8.2-alpha" ``` To run examples from your command line: diff --git a/google-cloud-logging/README.md b/google-cloud-logging/README.md index e439890b3b2c..796532d4a06d 100644 --- a/google-cloud-logging/README.md +++ b/google-cloud-logging/README.md @@ -26,16 +26,16 @@ Add this to your pom.xml file com.google.cloud google-cloud-logging - 0.8.0-beta + 0.8.2-beta ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-logging:0.8.0-beta' +compile 'com.google.cloud:google-cloud-logging:0.8.2-beta' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-logging" % "0.8.0-beta" +libraryDependencies += "com.google.cloud" % "google-cloud-logging" % "0.8.2-beta" ``` Example Application diff --git a/google-cloud-pubsub/README.md b/google-cloud-pubsub/README.md index 2b7cc5c9471d..23a299fffdd5 100644 --- a/google-cloud-pubsub/README.md +++ b/google-cloud-pubsub/README.md @@ -26,16 +26,16 @@ Add this to your pom.xml file com.google.cloud google-cloud-pubsub - 0.8.1-alpha + 0.8.2-alpha ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-pubsub:0.8.1-alpha' +compile 'com.google.cloud:google-cloud-pubsub:0.8.2-alpha' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-pubsub" % "0.8.1-alpha" +libraryDependencies += "com.google.cloud" % "google-cloud-pubsub" % "0.8.2-alpha" ``` Example Application diff --git a/google-cloud-resourcemanager/README.md b/google-cloud-resourcemanager/README.md index 6b6b3020c545..fd3e188a3e4f 100644 --- a/google-cloud-resourcemanager/README.md +++ b/google-cloud-resourcemanager/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-resourcemanager - 0.8.1-alpha + 0.8.2-alpha ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-resourcemanager:0.8.1-alpha' +compile 'com.google.cloud:google-cloud-resourcemanager:0.8.2-alpha' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-resourcemanager" % "0.8.1-alpha" +libraryDependencies += "com.google.cloud" % "google-cloud-resourcemanager" % "0.8.2-alpha" ``` Example Application diff --git a/google-cloud-storage/README.md b/google-cloud-storage/README.md index d415267d7a0e..1c6a22155c6e 100644 --- a/google-cloud-storage/README.md +++ b/google-cloud-storage/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-storage - 0.8.0-beta + 0.8.2-beta ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-storage:0.8.0-beta' +compile 'com.google.cloud:google-cloud-storage:0.8.2-beta' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-storage" % "0.8.0-beta" +libraryDependencies += "com.google.cloud" % "google-cloud-storage" % "0.8.2-beta" ``` Example Application diff --git a/google-cloud-translate/README.md b/google-cloud-translate/README.md index defae571ad91..cab062532488 100644 --- a/google-cloud-translate/README.md +++ b/google-cloud-translate/README.md @@ -22,16 +22,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud-translate - 0.8.1-alpha + 0.8.2-alpha ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-translate:0.8.1-alpha' +compile 'com.google.cloud:google-cloud-translate:0.8.2-alpha' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-translate" % "0.8.1-alpha" +libraryDependencies += "com.google.cloud" % "google-cloud-translate" % "0.8.2-alpha" ``` Example Application diff --git a/google-cloud/README.md b/google-cloud/README.md index 58c7a4d6756c..1584a288e364 100644 --- a/google-cloud/README.md +++ b/google-cloud/README.md @@ -27,16 +27,16 @@ If you are using Maven, add this to your pom.xml file com.google.cloud google-cloud - 0.8.1-alpha + 0.8.2-alpha ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud:0.8.1-alpha' +compile 'com.google.cloud:google-cloud:0.8.2-alpha' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud" % "0.8.1-alpha" +libraryDependencies += "com.google.cloud" % "google-cloud" % "0.8.2-alpha" ``` Troubleshooting From 30fd42d1b1aa0ae93b30f7ca271c4a1574227f8f Mon Sep 17 00:00:00 2001 From: garrettjonesgoogle Date: Wed, 1 Feb 2017 14:08:25 -0800 Subject: [PATCH 21/22] Fixing javadoc error in GaeFlexLoggingEnhancer (#1582) --- .../java/com/google/cloud/logging/GaeFlexLoggingEnhancer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/GaeFlexLoggingEnhancer.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/GaeFlexLoggingEnhancer.java index b4c80e288007..b710f03f67db 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/GaeFlexLoggingEnhancer.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/GaeFlexLoggingEnhancer.java @@ -21,10 +21,10 @@ import com.google.cloud.MonitoredResource.Builder; /** - * A Logging {@link Enhancer} that enhances the logging for the + * A {@link LoggingHandler.Enhancer} that enhances the logging for the * GAE Flex environment. This enhancer can * be configured in a logging.properties file with: - * + * *

      * handlers=com.google.cloud.logging.LoggingHandler
      * com.google.cloud.logging.LoggingHandler.log=gaeflex.log
    
    From 00df00b18762a5b8d70677c9e1603bb15bab2753 Mon Sep 17 00:00:00 2001
    From: Michael Darakananda 
    Date: Fri, 3 Feb 2017 16:30:38 +1100
    Subject: [PATCH 22/22] get tests to compile and pass
    
    ---
     .../cloud/examples/pubsub/PubSubExample.java  |  898 ++++++++++
     .../pubsub/snippets/PubSubSnippets.java       | 1050 ++++++++++++
     .../pubsub/snippets/SubscriptionSnippets.java |  316 ++++
     .../pubsub/snippets/TopicSnippets.java        |  332 ++++
     .../pubsub/snippets/ITPubSubSnippets.java     |  269 +++
     .../snippets/ITSubscriptionSnippets.java      |  125 ++
     .../pubsub/snippets/ITTopicSnippets.java      |  118 ++
     .../cloud/pubsub/AckDeadlineRenewer.java      |  317 ++++
     .../java/com/google/cloud/pubsub/Message.java |  454 ++++++
     .../java/com/google/cloud/pubsub/Option.java  |   77 +
     .../google/cloud/pubsub/PolicyMarshaller.java |   36 +
     .../java/com/google/cloud/pubsub/PubSub.java  | 1446 +++++++++++++++++
     .../google/cloud/pubsub/PubSubException.java  |   46 +
     .../google/cloud/pubsub/PubSubFactory.java    |   24 +
     .../google/cloud/pubsub/PubSubOptions.java    |  144 ++
     .../com/google/cloud/pubsub/PushConfig.java   |  363 +++++
     .../google/cloud/pubsub/ReceivedMessage.java  |  297 ++++
     .../com/google/cloud/pubsub/Subscription.java |  611 +++++++
     .../java/com/google/cloud/pubsub/Topic.java   |  556 +++++++
     .../com/google/cloud/pubsub/package-info.java |   54 +
     .../cloud/pubsub/spi/PubSubRpcFactory.java    |   27 +
     .../pubsub/spi/v1/MessageDispatcher.java      |    2 +-
     .../spi/v1/PollingSubscriberConnection.java   |    2 +-
     .../google/cloud/pubsub/spi/v1/Publisher.java |   22 +-
     .../cloud/pubsub/spi/v1/PublisherClient.java  |    5 +-
     .../spi/v1/StreamingSubscriberConnection.java |    2 +-
     .../cloud/pubsub/spi/v1/Subscriber.java       |   11 +-
     .../cloud/pubsub/spi/v1/SubscriberClient.java |    6 +-
     .../pubsub/testing/LocalPubSubHelper.java     |   23 +-
     .../cloud/pubsub/AckDeadlineRenewerTest.java  |  313 ++++
     .../google/cloud/pubsub/BaseSystemTest.java   |  837 ++++++++++
     .../cloud/pubsub/MessageConsumerImplTest.java |  453 ++++++
     .../com/google/cloud/pubsub/MessageTest.java  |  176 ++
     .../com/google/cloud/pubsub/OptionTest.java   |   66 +
     .../com/google/cloud/pubsub/PubSubTest.java   |   59 +
     .../google/cloud/pubsub/PushConfigTest.java   |  114 ++
     .../cloud/pubsub/ReceivedMessageTest.java     |  223 +++
     .../cloud/pubsub/SerializationTest.java       |  102 ++
     .../cloud/pubsub/SubscriptionIdTest.java      |   56 +
     .../cloud/pubsub/SubscriptionInfoTest.java    |  162 ++
     .../google/cloud/pubsub/SubscriptionTest.java |  436 +++++
     .../com/google/cloud/pubsub/TopicIdTest.java  |   86 +
     .../google/cloud/pubsub/TopicInfoTest.java    |   70 +
     .../com/google/cloud/pubsub/TopicTest.java    |  416 +++++
     .../google/cloud/pubsub/it/ITPubSubTest.java  |  144 ++
     .../google/cloud/pubsub/spi/v1/FakeClock.java |    8 +-
     .../pubsub/spi/v1/PublisherImplTest.java      |   41 +-
     .../pubsub/testing/LocalPubSubHelperTest.java |   19 +
     google-cloud-resourcemanager/README.md        |    1 +
     google-cloud/README.md                        |    4 +-
     pom.xml                                       |    2 +-
     utilities/verify.sh                           |    4 +-
     52 files changed, 11358 insertions(+), 67 deletions(-)
     create mode 100644 google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/PubSubExample.java
     create mode 100644 google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/snippets/PubSubSnippets.java
     create mode 100644 google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/snippets/SubscriptionSnippets.java
     create mode 100644 google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/snippets/TopicSnippets.java
     create mode 100644 google-cloud-examples/src/test/java/com/google/cloud/examples/pubsub/snippets/ITPubSubSnippets.java
     create mode 100644 google-cloud-examples/src/test/java/com/google/cloud/examples/pubsub/snippets/ITSubscriptionSnippets.java
     create mode 100644 google-cloud-examples/src/test/java/com/google/cloud/examples/pubsub/snippets/ITTopicSnippets.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/AckDeadlineRenewer.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Message.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Option.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PolicyMarshaller.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubException.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubFactory.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubOptions.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PushConfig.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Topic.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/package-info.java
     create mode 100644 google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpcFactory.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/AckDeadlineRenewerTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/BaseSystemTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/MessageConsumerImplTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/MessageTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/OptionTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/PubSubTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/PushConfigTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/ReceivedMessageTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SerializationTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SubscriptionIdTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SubscriptionInfoTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SubscriptionTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/TopicIdTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/TopicInfoTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/TopicTest.java
     create mode 100644 google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/it/ITPubSubTest.java
    
    diff --git a/google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/PubSubExample.java b/google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/PubSubExample.java
    new file mode 100644
    index 000000000000..7bb34839c4a3
    --- /dev/null
    +++ b/google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/PubSubExample.java
    @@ -0,0 +1,898 @@
    +/*
    + * Copyright 2016 Google Inc. All Rights Reserved.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + *       http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +
    +package com.google.cloud.examples.pubsub;
    +
    +import com.google.cloud.Identity;
    +import com.google.cloud.Policy;
    +import com.google.cloud.Role;
    +import com.google.cloud.pubsub.Message;
    +import com.google.cloud.pubsub.PubSub;
    +import com.google.cloud.pubsub.PubSub.MessageProcessor;
    +import com.google.cloud.pubsub.PubSubOptions;
    +import com.google.cloud.pubsub.PushConfig;
    +import com.google.cloud.pubsub.ReceivedMessage;
    +import com.google.cloud.pubsub.Subscription;
    +import com.google.cloud.pubsub.SubscriptionId;
    +import com.google.cloud.pubsub.SubscriptionInfo;
    +import com.google.cloud.pubsub.Topic;
    +import com.google.cloud.pubsub.TopicInfo;
    +import com.google.common.collect.ImmutableMap;
    +
    +import java.util.ArrayList;
    +import java.util.Arrays;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.concurrent.TimeUnit;
    +import java.util.concurrent.atomic.AtomicInteger;
    +
    +/**
    + * An example of using Google BigQuery.
    + *
    + * 

    This example demonstrates a simple/typical Pub/Sub usage. + * + *

    See the + * + * README for compilation instructions. Run this code with + *

    {@code target/appassembler/bin/PubSubExample
    + *  -Dexec.args="[]
    + *  pull async  ?
    + *  pull sync  
    + *  publish  +
    + *  replace-push-config  ?
    + *  ack  +
    + *  nack  +
    + *  create topic 
    + *  create subscription   ?
    + *  list subscriptions ?
    + *  list topics
    + *  delete topic 
    + *  delete subscription 
    + *  info topic 
    + *  info subscription 
    + *  get-policy topic 
    + *  get-policy subscription 
    + *  add-identity topic   
    + *  add-identity subscription   
    + *  test-permissions topic  +
    + *  test-permissions subscription  +"}
    + * + *

    The first parameter is an optional {@code project_id} (logged-in project will be used if not + * supplied). Second parameter is a Pub/Sub operation and can be used to demonstrate its usage. For + * operations that apply to more than one entity (`list`, `create`, `info` and `delete`) the third + * parameter specifies the entity. `pull` operation also takes a third parameter to specify whether + * pulling should be synchronous or asynchronous. + */ +public class PubSubExample { + + private static final Map CREATE_ACTIONS = new HashMap<>(); + private static final Map INFO_ACTIONS = new HashMap<>(); + private static final Map LIST_ACTIONS = new HashMap<>(); + private static final Map DELETE_ACTIONS = new HashMap<>(); + private static final Map PULL_ACTIONS = new HashMap<>(); + private static final Map GET_IAM_ACTIONS = new HashMap<>(); + private static final Map REPLACE_IAM_ACTIONS = new HashMap<>(); + private static final Map TEST_IAM_ACTIONS = new HashMap<>(); + private static final Map ACTIONS = new HashMap<>(); + + private abstract static class PubSubAction { + + abstract void run(PubSub pubsub, T arg) throws Exception; + + abstract T parse(String... args) throws Exception; + + protected String params() { + return ""; + } + } + + private static class Tuple { + + private final X x; + private final Y y; + + private Tuple(X x, Y y) { + this.x = x; + this.y = y; + } + + public static Tuple of(X x, Y y) { + return new Tuple<>(x, y); + } + + X x() { + return x; + } + + Y y() { + return y; + } + } + + private static class ParentAction extends PubSubAction> { + + private final Map subActions; + + ParentAction(Map subActions) { + this.subActions = ImmutableMap.copyOf(subActions); + } + + @Override + @SuppressWarnings("unchecked") + void run(PubSub pubsub, Tuple subaction) throws Exception { + subaction.x().run(pubsub, subaction.y()); + } + + @Override + Tuple parse(String... args) throws Exception { + if (args.length >= 1) { + PubSubAction action = subActions.get(args[0]); + if (action != null) { + Object actionArguments = action.parse(Arrays.copyOfRange(args, 1, args.length)); + return Tuple.of(action, actionArguments); + } else { + throw new IllegalArgumentException("Unrecognized entity '" + args[0] + "'."); + } + } + throw new IllegalArgumentException("Missing required entity."); + } + + @Override + public String params() { + StringBuilder builder = new StringBuilder(); + for (Map.Entry entry : subActions.entrySet()) { + builder.append('\n').append(entry.getKey()); + String param = entry.getValue().params(); + if (param != null && !param.isEmpty()) { + builder.append(' ').append(param); + } + } + return builder.toString(); + } + } + + private abstract static class NoArgsAction extends PubSubAction { + @Override + Void parse(String... args) throws Exception { + if (args.length == 0) { + return null; + } + throw new IllegalArgumentException("This action takes no arguments."); + } + } + + /** + * This class demonstrates how to list Pub/Sub topics. + * + * @see List + * topics in your project + */ + private static class ListTopicsAction extends NoArgsAction { + @Override + public void run(PubSub pubsub, Void arg) { + Iterator topicIterator = pubsub.listTopics().iterateAll(); + while (topicIterator.hasNext()) { + System.out.println(topicIterator.next()); + } + } + } + + private abstract static class TopicAction extends PubSubAction { + @Override + String parse(String... args) throws Exception { + String message; + if (args.length == 1) { + return args[0]; + } else if (args.length > 1) { + message = "Too many arguments."; + } else { + message = "Missing required topic name."; + } + throw new IllegalArgumentException(message); + } + + @Override + public String params() { + return ""; + } + } + + /** + * This class demonstrates how to retrieve information on a Pub/Sub topic. + */ + private static class TopicInfoAction extends TopicAction { + @Override + public void run(PubSub pubsub, String topic) { + System.out.printf("Topic info: %s%n", pubsub.getTopic(topic)); + } + } + + /** + * This class demonstrates how to create a Pub/Sub topic. + * + * @see Create a topic + */ + private static class CreateTopicAction extends TopicAction { + @Override + public void run(PubSub pubsub, String topic) { + pubsub.create(TopicInfo.of(topic)); + System.out.printf("Created topic %s%n", topic); + } + } + + /** + * This class demonstrates how to delete a Pub/Sub topic. + * + * @see Delete a topic + */ + private static class DeleteTopicAction extends TopicAction { + @Override + public void run(PubSub pubsub, String topic) { + pubsub.deleteTopic(topic); + System.out.printf("Deleted topic %s%n", topic); + } + } + + /** + * This class demonstrates how to list Pub/Sub subscriptions. + * + * @see List subscriptions + */ + private static class ListSubscriptionsAction extends PubSubAction { + @Override + public void run(PubSub pubsub, String topic) { + if (topic == null) { + Iterator subscriptionIterator = pubsub.listSubscriptions().iterateAll(); + while (subscriptionIterator.hasNext()) { + System.out.println(subscriptionIterator.next()); + } + } else { + Iterator subscriptionIdIterator = + pubsub.listSubscriptions(topic).iterateAll(); + while (subscriptionIdIterator.hasNext()) { + System.out.println(subscriptionIdIterator.next()); + } + } + } + + @Override + String parse(String... args) throws Exception { + if (args.length == 1) { + return args[0]; + } else if (args.length == 0) { + return null; + } else { + throw new IllegalArgumentException("Too many arguments."); + } + } + + @Override + public String params() { + return "?"; + } + } + + /** + * This class demonstrates how to publish messages to a Pub/Sub topic. + * + * @see Publish + * messages to a topic + */ + private static class PublishMessagesAction extends PubSubAction>> { + @Override + public void run(PubSub pubsub, Tuple> params) { + String topic = params.x(); + List messages = params.y(); + pubsub.publish(topic, messages); + System.out.printf("Published %d messages to topic %s%n", messages.size(), topic); + } + + @Override + Tuple> parse(String... args) throws Exception { + if (args.length < 2) { + throw new IllegalArgumentException("Missing required topic and messages"); + } + String topic = args[0]; + List messages = new ArrayList<>(); + for (String payload : Arrays.copyOfRange(args, 1, args.length)) { + messages.add(Message.of(payload)); + } + return Tuple.of(topic, messages); + } + + @Override + public String params() { + return " +"; + } + } + + private abstract static class SubscriptionAction extends PubSubAction { + @Override + String parse(String... args) throws Exception { + String message; + if (args.length == 1) { + return args[0]; + } else if (args.length > 1) { + message = "Too many arguments."; + } else { + message = "Missing required subscription name."; + } + throw new IllegalArgumentException(message); + } + + @Override + public String params() { + return ""; + } + } + + /** + * This class demonstrates how to retrieve information on a Pub/Sub subscription. + */ + private static class SubscriptionInfoAction extends SubscriptionAction { + @Override + public void run(PubSub pubsub, String subscription) { + System.out.printf("Subscription info: %s%n", pubsub.getSubscription(subscription)); + } + } + + /** + * This class demonstrates how to create a Pub/Sub subscription. + * + * @see Create a subscription + */ + private static class CreateSubscriptionAction extends PubSubAction { + @Override + public void run(PubSub pubsub, SubscriptionInfo subscription) { + pubsub.create(subscription); + System.out.printf("Created subscription %s%n", subscription.getName()); + } + + @Override + SubscriptionInfo parse(String... args) throws Exception { + String message; + if (args.length > 3) { + message = "Too many arguments."; + } else if (args.length < 2) { + message = "Missing required topic or subscription name"; + } else { + SubscriptionInfo.Builder builder = SubscriptionInfo.newBuilder(args[0], args[1]); + if (args.length == 3) { + builder.setPushConfig(PushConfig.of(args[2])); + } + return builder.build(); + } + throw new IllegalArgumentException(message); + } + + @Override + public String params() { + return " ?"; + } + } + + /** + * This class demonstrates how to delete a Pub/Sub subscription. + */ + private static class DeleteSubscriptionAction extends SubscriptionAction { + @Override + public void run(PubSub pubsub, String subscription) { + pubsub.deleteSubscription(subscription); + System.out.printf("Deleted subscription %s%n", subscription); + } + } + + /** + * This class demonstrates how to modify the push configuration for a Pub/Sub subscription. + * + * @see + * Switching between push and pull delivery + */ + private static class ReplacePushConfigAction extends PubSubAction> { + @Override + public void run(PubSub pubsub, Tuple params) { + String subscription = params.x(); + PushConfig pushConfig = params.y(); + pubsub.replacePushConfig(subscription, pushConfig); + System.out.printf("Set push config %s for subscription %s%n", pushConfig, subscription); + } + + @Override + Tuple parse(String... args) throws Exception { + String message; + if (args.length > 2) { + message = "Too many arguments."; + } else if (args.length < 1) { + message = "Missing required subscription name"; + } else { + String subscription = args[0]; + PushConfig pushConfig = null; + if (args.length == 2) { + pushConfig = PushConfig.of(args[1]); + } + return Tuple.of(subscription, pushConfig); + } + throw new IllegalArgumentException(message); + } + + @Override + public String params() { + return " ?"; + } + } + + private abstract static class MessagesAction extends PubSubAction>> { + @Override + Tuple> parse(String... args) throws Exception { + if (args.length < 2) { + throw new IllegalArgumentException("Missing required subscription and ack IDs"); + } + String subscription = args[0]; + return Tuple.of(subscription, Arrays.asList(Arrays.copyOfRange(args, 1, args.length))); + } + + @Override + public String params() { + return " +"; + } + } + + /** + * This class demonstrates how to acknowledge Pub/Sub messages for a subscription. + * + * @see Receiving + * pull messages + */ + private static class AckMessagesAction extends MessagesAction { + @Override + public void run(PubSub pubsub, Tuple> params) { + String subscription = params.x(); + List ackIds = params.y(); + pubsub.ack(subscription, ackIds); + System.out.printf("Acked %d messages for subscription %s%n", ackIds.size(), subscription); + } + } + + /** + * This class demonstrates how to "nack" Pub/Sub messages for a subscription. This action + * corresponds to setting the acknowledge deadline to 0. + * + * @see Message + * acknowledgement deadline + */ + private static class NackMessagesAction extends MessagesAction { + @Override + public void run(PubSub pubsub, Tuple> params) { + String subscription = params.x(); + List ackIds = params.y(); + pubsub.nack(subscription, ackIds); + System.out.printf("Nacked %d messages for subscription %s%n", ackIds.size(), subscription); + } + } + + /** + * This class demonstrates how modify the acknowledge deadline for messages in a Pub/Sub + * subscription. + * + * @see Message + * acknowledgement deadline + */ + private static class ModifyAckDeadlineAction + extends PubSubAction>> { + + static class SubscriptionAndDeadline { + + private final String subscription; + private final int deadlineMillis; + + private SubscriptionAndDeadline(String subscription, int deadlineMillis) { + this.subscription = subscription; + this.deadlineMillis = deadlineMillis; + } + + String subscription() { + return subscription; + } + + int deadlineMillis() { + return deadlineMillis; + } + } + + @Override + public void run(PubSub pubsub, Tuple> params) + throws Exception { + String subscription = params.x().subscription(); + int deadline = params.x().deadlineMillis(); + List ackIds = params.y(); + pubsub.modifyAckDeadline(subscription, deadline, TimeUnit.MILLISECONDS, ackIds); + System.out.printf("Ack deadline set to %d for %d messages in subscription %s%n", deadline, + ackIds.size(), subscription); + } + + @Override + Tuple> parse(String... args) throws Exception { + if (args.length < 3) { + throw new IllegalArgumentException("Missing required subscription, deadline and ack IDs"); + } + String subscription = args[0]; + int deadline = Integer.parseInt(args[1]); + return Tuple.of(new SubscriptionAndDeadline(subscription, deadline), + Arrays.asList(Arrays.copyOfRange(args, 2, args.length))); + } + + @Override + public String params() { + return " +"; + } + } + + /** + * This class demonstrates how to asynchronously pull messages from a Pub/Sub pull subscription. + * Messages are pulled until a timeout is reached. + * + * @see Receiving + * pull messages + */ + private static class PullAsyncAction extends PubSubAction> { + @Override + public void run(PubSub pubsub, Tuple params) throws Exception { + String subscription = params.x(); + Long timeout = params.y(); + final AtomicInteger messageCount = new AtomicInteger(); + MessageProcessor messageProcessor = new MessageProcessor() { + + @Override + public void process(Message message) throws Exception { + System.out.printf("Received message \"%s\"%n", message); + messageCount.incrementAndGet(); + } + }; + try (PubSub.MessageConsumer consumer = pubsub.pullAsync(subscription, messageProcessor)) { + Thread.sleep(timeout); + } + System.out.printf("Pulled %d messages from subscription %s%n", messageCount.get(), + subscription); + } + + @Override + Tuple parse(String... args) throws Exception { + String message; + if (args.length > 2) { + message = "Too many arguments."; + } else if (args.length < 1) { + message = "Missing required subscription name"; + } else { + String subscription = args[0]; + long timeout = 60_000; + if (args.length == 2) { + timeout = Long.parseLong(args[1]); + } + return Tuple.of(subscription, timeout); + } + throw new IllegalArgumentException(message); + } + + @Override + public String params() { + return " ?"; + } + } + + /** + * This class demonstrates how to synchronously pull messages from a Pub/Sub pull subscription. + * No more than the requested number of messages are pulled. Possibly less messages are pulled. + * + * @see Receiving + * pull messages + */ + private static class PullSyncAction extends PubSubAction> { + @Override + public void run(PubSub pubsub, Tuple params) throws Exception { + String subscription = params.x(); + Integer maxMessages = params.y(); + Iterator messageIterator = pubsub.pull(subscription, maxMessages); + int messageCount = 0; + while (messageIterator.hasNext()) { + ReceivedMessage message = messageIterator.next(); + System.out.printf("Received message \"%s\"%n", message); + message.ack(); + messageCount++; + } + System.out.printf("Pulled %d messages from subscription %s%n", messageCount, subscription); + } + + @Override + Tuple parse(String... args) throws Exception { + String message; + if (args.length == 2) { + String subscription = args[0]; + int maxMessages = Integer.parseInt(args[1]); + return Tuple.of(subscription, maxMessages); + } else if (args.length > 2) { + message = "Too many arguments."; + } else { + message = "Missing required subscription name"; + } + throw new IllegalArgumentException(message); + } + + @Override + public String params() { + return " "; + } + } + + private abstract static class GetPolicyAction extends PubSubAction { + @Override + String parse(String... args) throws Exception { + String message; + if (args.length == 1) { + return args[0]; + } else if (args.length > 1) { + message = "Too many arguments."; + } else { + message = "Missing required resource name"; + } + throw new IllegalArgumentException(message); + } + + @Override + public String params() { + return ""; + } + } + + /** + * This class demonstrates how to get the IAM policy of a topic. + * + * @see Access Control + */ + private static class GetTopicPolicyAction extends GetPolicyAction { + @Override + public void run(PubSub pubsub, String topic) throws Exception { + Policy policy = pubsub.getTopicPolicy(topic); + System.out.printf("Policy for topic %s%n", topic); + System.out.println(policy); + } + } + + /** + * This class demonstrates how to get the IAM policy of a subscription. + * + * @see Access Control + */ + private static class GetSubscriptionPolicyAction extends GetPolicyAction { + @Override + public void run(PubSub pubsub, String subscription) throws Exception { + Policy policy = pubsub.getSubscriptionPolicy(subscription); + System.out.printf("Policy for subscription %s%n", subscription); + System.out.println(policy); + } + } + + private abstract static class AddIdentityAction + extends PubSubAction>> { + @Override + Tuple> parse(String... args) throws Exception { + String message; + if (args.length == 3) { + String resourceName = args[0]; + Role role = Role.of(args[1]); + Identity identity = Identity.valueOf(args[2]); + return Tuple.of(resourceName, Tuple.of(role, identity)); + } else if (args.length > 2) { + message = "Too many arguments."; + } else { + message = "Missing required resource name, role and identity"; + } + throw new IllegalArgumentException(message); + } + + @Override + public String params() { + return " "; + } + } + + /** + * This class demonstrates how to add an identity to a certain role in a topic's IAM policy. + * + * @see Access Control + */ + private static class AddIdentityTopicAction extends AddIdentityAction { + @Override + public void run(PubSub pubsub, Tuple> param) throws Exception { + String topic = param.x(); + Tuple roleAndIdentity = param.y(); + Role role = roleAndIdentity.x(); + Identity identity = roleAndIdentity.y(); + Policy policy = pubsub.getTopicPolicy(topic); + policy = pubsub.replaceTopicPolicy(topic, + policy.toBuilder().addIdentity(role, identity).build()); + System.out.printf("Added role %s to identity %s for topic %s%n", role, identity, topic); + System.out.println(policy); + } + } + + /** + * This class demonstrates how to add an identity to a certain role in a subscription's IAM + * policy. + * + * @see Access Control + */ + private static class AddIdentitySubscriptionAction extends AddIdentityAction { + @Override + public void run(PubSub pubsub, Tuple> param) throws Exception { + String subscription = param.x(); + Tuple roleAndIdentity = param.y(); + Role role = roleAndIdentity.x(); + Identity identity = roleAndIdentity.y(); + Policy policy = pubsub.getSubscriptionPolicy(subscription); + policy = pubsub.replaceSubscriptionPolicy(subscription, + policy.toBuilder().addIdentity(role, identity).build()); + System.out.printf("Added role %s to identity %s for subscription %s%n", role, identity, + subscription); + System.out.println(policy); + } + } + + private abstract static class TestPermissionsAction + extends PubSubAction>> { + @Override + Tuple> parse(String... args) throws Exception { + if (args.length >= 2) { + String resourceName = args[0]; + return Tuple.of(resourceName, Arrays.asList(Arrays.copyOfRange(args, 1, args.length))); + } + throw new IllegalArgumentException("Missing required resource name and permissions"); + } + + @Override + public String params() { + return " +"; + } + } + + /** + * This class demonstrates how to test whether the caller has the provided permissions on a topic. + * + * @see Access Control + */ + private static class TestTopicPermissionsAction extends TestPermissionsAction { + @Override + public void run(PubSub pubsub, Tuple> param) throws Exception { + String topic = param.x(); + List permissions = param.y(); + List booleanPermissions = pubsub.testTopicPermissions(topic, permissions); + System.out.printf("Caller permissions on topic %s%n", topic); + for (int i = 0; i < permissions.size(); i++) { + System.out.printf("%s: %b%n", permissions.get(i), booleanPermissions.get(i)); + } + } + } + + /** + * This class demonstrates how to test whether the caller has the provided permissions on a + * subscription. + * + * @see Access Control + */ + private static class TestSubscriptionPermissionsAction extends TestPermissionsAction { + @Override + public void run(PubSub pubsub, Tuple> param) throws Exception { + String subscription = param.x(); + List permissions = param.y(); + List booleanPermissions = + pubsub.testSubscriptionPermissions(subscription, permissions); + System.out.printf("Caller permissions on subscription %s%n", subscription); + for (int i = 0; i < permissions.size(); i++) { + System.out.printf("%s: %b%n", permissions.get(i), booleanPermissions.get(i)); + } + } + } + + static { + CREATE_ACTIONS.put("topic", new CreateTopicAction()); + CREATE_ACTIONS.put("subscription", new CreateSubscriptionAction()); + INFO_ACTIONS.put("topic", new TopicInfoAction()); + INFO_ACTIONS.put("subscription", new SubscriptionInfoAction()); + LIST_ACTIONS.put("topics", new ListTopicsAction()); + LIST_ACTIONS.put("subscriptions", new ListSubscriptionsAction()); + DELETE_ACTIONS.put("topic", new DeleteTopicAction()); + DELETE_ACTIONS.put("subscription", new DeleteSubscriptionAction()); + PULL_ACTIONS.put("async", new PullAsyncAction()); + PULL_ACTIONS.put("sync", new PullSyncAction()); + GET_IAM_ACTIONS.put("topic", new GetTopicPolicyAction()); + GET_IAM_ACTIONS.put("subscription", new GetSubscriptionPolicyAction()); + REPLACE_IAM_ACTIONS.put("topic", new AddIdentityTopicAction()); + REPLACE_IAM_ACTIONS.put("subscription", new AddIdentitySubscriptionAction()); + TEST_IAM_ACTIONS.put("topic", new TestTopicPermissionsAction()); + TEST_IAM_ACTIONS.put("subscription", new TestSubscriptionPermissionsAction()); + ACTIONS.put("create", new ParentAction(CREATE_ACTIONS)); + ACTIONS.put("info", new ParentAction(INFO_ACTIONS)); + ACTIONS.put("list", new ParentAction(LIST_ACTIONS)); + ACTIONS.put("delete", new ParentAction(DELETE_ACTIONS)); + ACTIONS.put("pull", new ParentAction(PULL_ACTIONS)); + ACTIONS.put("get-policy", new ParentAction(GET_IAM_ACTIONS)); + ACTIONS.put("add-identity", new ParentAction(REPLACE_IAM_ACTIONS)); + ACTIONS.put("test-permissions", new ParentAction(TEST_IAM_ACTIONS)); + ACTIONS.put("publish", new PublishMessagesAction()); + ACTIONS.put("replace-push-config", new ReplacePushConfigAction()); + ACTIONS.put("ack", new AckMessagesAction()); + ACTIONS.put("nack", new NackMessagesAction()); + ACTIONS.put("modify-ack-deadline", new ModifyAckDeadlineAction()); + } + + private static void printUsage() { + StringBuilder actionAndParams = new StringBuilder(); + for (Map.Entry entry : ACTIONS.entrySet()) { + actionAndParams.append("\n\t").append(entry.getKey()); + + String param = entry.getValue().params(); + if (param != null && !param.isEmpty()) { + actionAndParams.append(' ').append(param.replace("\n", "\n\t\t")); + } + } + System.out.printf("Usage: %s [] operation [entity] *%s%n", + PubSubExample.class.getSimpleName(), actionAndParams); + } + + @SuppressWarnings("unchecked") + public static void main(String... args) throws Exception { + if (args.length < 1) { + System.out.println("Missing required project id and action"); + printUsage(); + return; + } + PubSubOptions.Builder optionsBuilder = PubSubOptions.newBuilder(); + PubSubAction action; + String actionName; + if (args.length >= 2 && !ACTIONS.containsKey(args[0])) { + actionName = args[1]; + optionsBuilder.setProjectId(args[0]); + action = ACTIONS.get(args[1]); + args = Arrays.copyOfRange(args, 2, args.length); + } else { + actionName = args[0]; + action = ACTIONS.get(args[0]); + args = Arrays.copyOfRange(args, 1, args.length); + } + if (action == null) { + System.out.println("Unrecognized action."); + printUsage(); + return; + } + try (PubSub pubsub = optionsBuilder.build().getService()) { + Object arg; + try { + arg = action.parse(args); + } catch (IllegalArgumentException ex) { + System.out.printf("Invalid input for action '%s'. %s%n", actionName, ex.getMessage()); + System.out.printf("Expected: %s%n", action.params()); + return; + } catch (Exception ex) { + System.out.println("Failed to parse arguments."); + ex.printStackTrace(); + return; + } + action.run(pubsub, arg); + } + } +} diff --git a/google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/snippets/PubSubSnippets.java b/google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/snippets/PubSubSnippets.java new file mode 100644 index 000000000000..d03cbe8347c8 --- /dev/null +++ b/google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/snippets/PubSubSnippets.java @@ -0,0 +1,1050 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * EDITING INSTRUCTIONS + * This file is referenced in PubSub's javadoc. Any change to this file should be reflected in + * PubSub's javadoc. + */ + +package com.google.cloud.examples.pubsub.snippets; + +import com.google.cloud.AsyncPage; +import com.google.cloud.Identity; +import com.google.cloud.Page; +import com.google.cloud.Policy; +import com.google.cloud.Role; +import com.google.cloud.pubsub.Message; +import com.google.cloud.pubsub.PubSub; +import com.google.cloud.pubsub.PubSub.ListOption; +import com.google.cloud.pubsub.PubSub.MessageProcessor; +import com.google.cloud.pubsub.PushConfig; +import com.google.cloud.pubsub.ReceivedMessage; +import com.google.cloud.pubsub.Subscription; +import com.google.cloud.pubsub.SubscriptionId; +import com.google.cloud.pubsub.SubscriptionInfo; +import com.google.cloud.pubsub.Topic; +import com.google.cloud.pubsub.TopicInfo; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * This class contains a number of snippets for the {@link PubSub} interface. + */ +public class PubSubSnippets { + + private final PubSub pubsub; + + public PubSubSnippets(PubSub pubsub) { + this.pubsub = pubsub; + } + + /** + * Example of creating a topic. + */ + // [TARGET create(TopicInfo)] + // [VARIABLE "my_topic_name"] + public Topic createTopic(String topicName) { + // [START createTopic] + TopicInfo topicInfo = TopicInfo.of(topicName); + Topic topic = pubsub.create(topicInfo); + // [END createTopic] + return topic; + } + + /** + * Example of asynchronously creating a topic. + */ + // [TARGET createAsync(TopicInfo)] + // [VARIABLE "my_topic_name"] + public Topic createTopicAsync(String topicName) throws ExecutionException, InterruptedException { + // [START createTopicAsync] + TopicInfo topicInfo = TopicInfo.of(topicName); + Future future = pubsub.createAsync(topicInfo); + // ... + Topic topic = future.get(); + // [END createTopicAsync] + return topic; + } + + /** + * Example of getting a topic. + */ + // [TARGET getTopic(String)] + // [VARIABLE "my_topic_name"] + public Topic getTopic(String topicName) { + // [START getTopic] + Topic topic = pubsub.getTopic(topicName); + if (topic == null) { + // topic was not found + } + // [END getTopic] + return topic; + } + + /** + * Example of asynchronously getting a topic. + */ + // [TARGET getTopicAsync(String)] + // [VARIABLE "my_topic_name"] + public Topic getTopicAsync(String topicName) throws ExecutionException, InterruptedException { + // [START getTopicAsync] + Future future = pubsub.getTopicAsync(topicName); + // ... + Topic topic = future.get(); + if (topic == null) { + // topic was not found + } + // [END getTopicAsync] + return topic; + } + + /** + * Example of listing topics, specifying the page size. + */ + // [TARGET listTopics(ListOption...)] + public Page listTopics() { + // [START listTopics] + Page topics = pubsub.listTopics(ListOption.pageSize(100)); + Iterator topicIterator = topics.iterateAll(); + while (topicIterator.hasNext()) { + Topic topic = topicIterator.next(); + // do something with the topic + } + // [END listTopics] + return topics; + } + + /** + * Example of asynchronously listing topics, specifying the page size. + */ + // [TARGET listTopicsAsync(ListOption...)] + public Page listTopicsAsync() throws ExecutionException, InterruptedException { + // [START listTopicsAsync] + Future> future = pubsub.listTopicsAsync(ListOption.pageSize(100)); + // ... + AsyncPage topics = future.get(); + Iterator topicIterator = topics.iterateAll(); + while (topicIterator.hasNext()) { + Topic topic = topicIterator.next(); + // do something with the topic + } + // [END listTopicsAsync] + return topics; + } + + /** + * Example of deleting a topic. + */ + // [TARGET deleteTopic(String)] + // [VARIABLE "my_topic_name"] + public boolean deleteTopic(String topicName) { + // [START deleteTopic] + boolean deleted = pubsub.deleteTopic(topicName); + if (deleted) { + // the topic was deleted + } else { + // the topic was not found + } + // [END deleteTopic] + return deleted; + } + + /** + * Example of asynchronously deleting a topic. + */ + // [TARGET deleteTopicAsync(String)] + // [VARIABLE "my_topic_name"] + public boolean deleteTopicAsync(String topicName) + throws ExecutionException, InterruptedException { + // [START deleteTopicAsync] + Future future = pubsub.deleteTopicAsync(topicName); + // ... + boolean deleted = future.get(); + if (deleted) { + // the topic was deleted + } else { + // the topic was not found + } + // [END deleteTopicAsync] + return deleted; + } + + /** + * Example of publishing one message to a topic. + */ + // [TARGET publish(String, Message)] + // [VARIABLE "my_topic_name"] + public String publishOneMessage(String topicName) { + // [START publishOneMessage] + Message message = Message.of("payload"); + String messageId = pubsub.publish(topicName, message); + // [END publishOneMessage] + return messageId; + } + + /** + * Example of asynchronously publishing one message to a topic. + */ + // [TARGET publishAsync(String, Message)] + // [VARIABLE "my_topic_name"] + public String publishOneMessageAsync(String topicName) + throws ExecutionException, InterruptedException { + // [START publishOneMessageAsync] + Message message = Message.of("payload"); + Future future = pubsub.publishAsync(topicName, message); + // ... + String messageId = future.get(); + // [END publishOneMessageAsync] + return messageId; + } + + /** + * Example of publishing a list of messages to a topic. + */ + // [TARGET publish(String, Iterable)] + // [VARIABLE "my_topic_name"] + public List publishMessageList(String topicName) { + // [START publishMessageList] + List messages = new LinkedList<>(); + messages.add(Message.of("payload1")); + messages.add(Message.of("payload2")); + List messageIds = pubsub.publish(topicName, messages); + // [END publishMessageList] + return messageIds; + } + + /** + * Example of asynchronously publishing a list of messages to a topic. + */ + // [TARGET publishAsync(String, Iterable)] + // [VARIABLE "my_topic_name"] + public List publishMessageListAsync(String topicName) + throws ExecutionException, InterruptedException { + // [START publishMessageListAsync] + List messages = new LinkedList<>(); + messages.add(Message.of("payload1")); + messages.add(Message.of("payload2")); + Future> future = pubsub.publishAsync(topicName, messages); + // ... + List messageIds = future.get(); + // [END publishMessageListAsync] + return messageIds; + } + + /** + * Example of publishing some messages to a topic. + */ + // [TARGET publish(String, Message, Message...)] + // [VARIABLE "my_topic_name"] + public List publishMessages(String topicName) { + // [START publishMessages] + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + List messageIds = pubsub.publish(topicName, message1, message2); + // [END publishMessages] + return messageIds; + } + + /** + * Example of asynchronously publishing some messages to a topic. + */ + // [TARGET publishAsync(String, Message, Message...)] + // [VARIABLE "my_topic_name"] + public List publishMessagesAsync(String topicName) + throws ExecutionException, InterruptedException { + // [START publishMessagesAsync] + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + Future> future = pubsub.publishAsync(topicName, message1, message2); + // ... + List messageIds = future.get(); + // [END publishMessagesAsync] + return messageIds; + } + + /** + * Example of creating a pull subscription for a topic. + */ + // [TARGET create(SubscriptionInfo)] + // [VARIABLE "my_topic_name"] + // [VARIABLE "my_subscription_name"] + public Subscription createSubscription(String topicName, String subscriptionName) { + // [START createSubscription] + SubscriptionInfo subscriptionInfo = SubscriptionInfo.of(topicName, subscriptionName); + Subscription subscription = pubsub.create(subscriptionInfo); + // [END createSubscription] + return subscription; + } + + /** + * Example of asynchronously creating a pull subscription for a topic. + */ + // [TARGET createAsync(SubscriptionInfo)] + // [VARIABLE "my_topic_name"] + // [VARIABLE "my_subscription_name"] + public Subscription createSubscriptionAsync(String topicName, String subscriptionName) + throws ExecutionException, InterruptedException { + // [START createSubscriptionAsync] + SubscriptionInfo subscriptionInfo = SubscriptionInfo.of(topicName, subscriptionName); + Future future = pubsub.createAsync(subscriptionInfo); + // ... + Subscription subscription = future.get(); + // [END createSubscriptionAsync] + return subscription; + } + + /** + * Example of replacing the push configuration of a subscription, setting the push endpoint. + */ + // [TARGET replacePushConfig(String, PushConfig)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "https://www.example.com/push"] + public void replacePushConfig(String subscriptionName, String endpoint) { + // [START replacePushConfig] + PushConfig pushConfig = PushConfig.of(endpoint); + pubsub.replacePushConfig(subscriptionName, pushConfig); + // [END replacePushConfig] + } + + /** + * Example of replacing the push configuration of a subscription, making it a pull + * subscription. + */ + // [TARGET replacePushConfig(String, PushConfig)] + // [VARIABLE "my_subscription_name"] + public void replacePushConfigToPull(String subscriptionName) { + // [START replacePushConfigToPull] + pubsub.replacePushConfig(subscriptionName, null); + // [END replacePushConfigToPull] + } + + /** + * Example of asynchronously replacing the push configuration of a subscription, setting the + * push endpoint. + */ + // [TARGET replacePushConfigAsync(String, PushConfig)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "https://www.example.com/push"] + public void replacePushConfigAsync(String subscriptionName, String endpoint) + throws ExecutionException, InterruptedException { + // [START replacePushConfigAsync] + PushConfig pushConfig = PushConfig.of(endpoint); + Future future = pubsub.replacePushConfigAsync(subscriptionName, pushConfig); + // ... + future.get(); + // [END replacePushConfigAsync] + } + + /** + * Example of asynchronously replacing the push configuration of a subscription, making it a + * pull subscription. + */ + // [TARGET replacePushConfigAsync(String, PushConfig)] + // [VARIABLE "my_subscription_name"] + public void replacePushConfigToPullAsync(String subscriptionName) + throws ExecutionException, InterruptedException { + // [START replacePushConfigToPullAsync] + Future future = pubsub.replacePushConfigAsync(subscriptionName, null); + // ... + future.get(); + // [END replacePushConfigToPullAsync] + } + + /** + * Example of getting a subscription. + */ + // [TARGET getSubscription(String)] + // [VARIABLE "my_subscription_name"] + public Subscription getSubscription(String subscriptionName) { + // [START getSubscription] + Subscription subscription = pubsub.getSubscription(subscriptionName); + if (subscription == null) { + // subscription was not found + } + // [END getSubscription] + return subscription; + } + + /** + * Example of asynchronously getting a subscription. + */ + // [TARGET getSubscriptionAsync(String)] + // [VARIABLE "my_subscription_name"] + public Subscription getSubscriptionAsync(String subscriptionName) + throws ExecutionException, InterruptedException { + // [START getSubscriptionAsync] + Future future = pubsub.getSubscriptionAsync(subscriptionName); + // ... + Subscription subscription = future.get(); + if (subscription == null) { + // subscription was not found + } + // [END getSubscriptionAsync] + return subscription; + } + + /** + * Example of listing subscriptions, specifying the page size. + */ + // [TARGET listSubscriptions(ListOption...)] + public Page listSubscriptions() { + // [START listSubscriptions] + Page subscriptions = pubsub.listSubscriptions(ListOption.pageSize(100)); + Iterator subscriptionIterator = subscriptions.iterateAll(); + while (subscriptionIterator.hasNext()) { + Subscription subscription = subscriptionIterator.next(); + // do something with the subscription + } + // [END listSubscriptions] + return subscriptions; + } + + /** + * Example of asynchronously listing subscriptions, specifying the page size. + */ + // [TARGET listSubscriptionsAsync(ListOption...)] + public Page listSubscriptionsAsync() throws ExecutionException, InterruptedException { + // [START listSubscriptionsAsync] + Future> future = + pubsub.listSubscriptionsAsync(ListOption.pageSize(100)); + // ... + AsyncPage subscriptions = future.get(); + Iterator subscriptionIterator = subscriptions.iterateAll(); + while (subscriptionIterator.hasNext()) { + Subscription subscription = subscriptionIterator.next(); + // do something with the subscription + } + // [END listSubscriptionsAsync] + return subscriptions; + } + + /** + * Example of listing subscriptions for a topic, specifying the page size. + */ + // [TARGET listSubscriptions(String, ListOption...)] + // [VARIABLE "my_topic_name"] + public Page listSubscriptionsForTopic(String topicName) { + // [START listSubscriptionsForTopic] + Page subscriptions = + pubsub.listSubscriptions(topicName, ListOption.pageSize(100)); + Iterator subscriptionIterator = subscriptions.iterateAll(); + while (subscriptionIterator.hasNext()) { + SubscriptionId subscription = subscriptionIterator.next(); + // do something with the subscription identity + } + // [END listSubscriptionsForTopic] + return subscriptions; + } + + /** + * Example of asynchronously listing subscriptions for a topic, specifying the page size. + */ + // [TARGET listSubscriptionsAsync(String, ListOption...)] + // [VARIABLE "my_topic_name"] + public Page listSubscriptionsForTopicAsync(String topicName) + throws ExecutionException, InterruptedException { + // [START listSubscriptionsForTopicAsync] + Future> future = + pubsub.listSubscriptionsAsync(topicName, ListOption.pageSize(100)); + // ... + AsyncPage subscriptions = future.get(); + Iterator subscriptionIterator = subscriptions.iterateAll(); + while (subscriptionIterator.hasNext()) { + SubscriptionId subscription = subscriptionIterator.next(); + // do something with the subscription identity + } + // [END listSubscriptionsForTopicAsync] + return subscriptions; + } + + /** + * Example of deleting a subscription. + */ + // [TARGET deleteSubscription(String)] + // [VARIABLE "my_subscription_name"] + public boolean deleteSubscription(String subscriptionName) { + // [START deleteSubscription] + boolean deleted = pubsub.deleteSubscription(subscriptionName); + if (deleted) { + // the subscription was deleted + } else { + // the subscription was not found + } + // [END deleteSubscription] + return deleted; + } + + /** + * Example of asynchronously deleting a subscription. + */ + // [TARGET deleteSubscriptionAsync(String)] + // [VARIABLE "my_subscription_name"] + public boolean deleteSubscriptionAsync(String subscriptionName) + throws ExecutionException, InterruptedException { + // [START deleteSubscriptionAsync] + Future future = pubsub.deleteSubscriptionAsync(subscriptionName); + // ... + boolean deleted = future.get(); + if (deleted) { + // the subscription was deleted + } else { + // the subscription was not found + } + // [END deleteSubscriptionAsync] + return deleted; + } + + /** + * Example of pulling a maximum number of messages from a subscription. + */ + // [TARGET pull(String, int)] + // [VARIABLE "my_subscription_name"] + public void pull(String subscriptionName) { + // [START pull] + Iterator messages = pubsub.pull(subscriptionName, 100); + // Ack deadline is renewed until the message is consumed + while (messages.hasNext()) { + ReceivedMessage message = messages.next(); + // do something with message and ack/nack it + message.ack(); // or message.nack() + } + // [END pull] + } + + /** + * Example of asynchronously pulling a maximum number of messages from a subscription. + */ + // [TARGET pullAsync(String, int)] + // [VARIABLE "my_subscription_name"] + public void pullAsync(String subscriptionName) throws ExecutionException, InterruptedException { + // [START pullAsync] + Future> future = pubsub.pullAsync(subscriptionName, 100); + // ... + Iterator messages = future.get(); + // Ack deadline is renewed until the message is consumed + while (messages.hasNext()) { + ReceivedMessage message = messages.next(); + // do something with message and ack/nack it + message.ack(); // or message.nack() + } + // [END pullAsync] + } + + /** + * Example of continuously pulling messages from a subscription. + */ + // [TARGET pullAsync(String, MessageProcessor, PullOption...)] + // [VARIABLE "my_subscription_name"] + public void pullWithMessageConsumer(String subscriptionName) throws Exception { + // [START pullWithMessageConsumer] + MessageProcessor callback = new MessageProcessor() { + public void process(Message message) throws Exception { + // Ack deadline is renewed until this method returns + // Message is acked if this method returns successfully + // Message is nacked if this method throws an exception + } + }; + PubSub.MessageConsumer consumer = pubsub.pullAsync(subscriptionName, callback); + // ... + // Stop pulling + consumer.close(); + // [END pullWithMessageConsumer] + } + + /** + * Example of acking one message. + */ + // [TARGET ack(String, String, String...)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message_ack_id"] + public void ackOneMessage(String subscriptionName, String ackId) { + // [START ackOneMessage] + pubsub.ack(subscriptionName, ackId); + // [END ackOneMessage] + } + + /** + * Example of asynchronously acking one message. + */ + // [TARGET ackAsync(String, String, String...)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message_ack_id"] + public void ackOneMessageAsync(String subscriptionName, String ackId) + throws ExecutionException, InterruptedException { + // [START ackOneMessageAsync] + Future future = pubsub.ackAsync(subscriptionName, ackId); + // ... + future.get(); + // [END ackOneMessageAsync] + } + + /** + * Example of acking more messages. + */ + // [TARGET ack(String, String, String...)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message1_ack_id"] + // [VARIABLE "message2_ack_id"] + public void ackMoreMessages(String subscriptionName, String ackId1, String ackId2) { + // [START ackMoreMessages] + pubsub.ack(subscriptionName, ackId1, ackId2); + // [END ackMoreMessages] + } + + /** + * Example of asynchronously acking more messages. + */ + // [TARGET ackAsync(String, String, String...)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message1_ack_id"] + // [VARIABLE "message2_ack_id"] + public void ackMoreMessagesAsync(String subscriptionName, String ackId1, String ackId2) + throws ExecutionException, InterruptedException { + // [START ackMoreMessagesAsync] + Future future = pubsub.ackAsync(subscriptionName, ackId1, ackId2); + // ... + future.get(); + // [END ackMoreMessagesAsync] + } + + /** + * Example of acking a list of messages. + */ + // [TARGET ack(String, Iterable)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message1_ack_id"] + // [VARIABLE "message2_ack_id"] + public void ackMessageList(String subscriptionName, String ackId1, String ackId2) { + // [START ackMessageList] + List ackIds = new LinkedList<>(); + ackIds.add(ackId1); + ackIds.add(ackId2); + pubsub.ack(subscriptionName, ackIds); + // [END ackMessageList] + } + + /** + * Example of asynchronously acking a list of messages. + */ + // [TARGET ackAsync(String, Iterable)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message1_ack_id"] + // [VARIABLE "message2_ack_id"] + public void ackMessageListAsync(String subscriptionName, String ackId1, String ackId2) + throws ExecutionException, InterruptedException { + // [START ackMessageListAsync] + List ackIds = new LinkedList<>(); + ackIds.add(ackId1); + ackIds.add(ackId2); + Future future = pubsub.ackAsync(subscriptionName, ackIds); + // ... + future.get(); + // [END ackMessageListAsync] + } + + /** + * Example of nacking one message. + */ + // [TARGET nack(String, String, String...)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message_ack_id"] + public void nackOneMessage(String subscriptionName, String ackId) { + // [START nackOneMessage] + pubsub.nack(subscriptionName, ackId); + // [END nackOneMessage] + } + + /** + * Example of asynchronously nacking one message. + */ + // [TARGET nackAsync(String, String, String...)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message_ack_id"] + public void nackOneMessageAsync(String subscriptionName, String ackId) + throws ExecutionException, InterruptedException { + // [START nackOneMessageAsync] + Future future = pubsub.nackAsync(subscriptionName, ackId); + // ... + future.get(); + // [END nackOneMessageAsync] + } + + /** + * Example of nacking more messages. + */ + // [TARGET nack(String, String, String...)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message1_ack_id"] + // [VARIABLE "message2_ack_id"] + public void nackMoreMessages(String subscriptionName, String ackId1, String ackId2) { + // [START nackMoreMessages] + pubsub.nack(subscriptionName, ackId1, ackId2); + // [END nackMoreMessages] + } + + /** + * Example of asynchronously nacking more messages. + */ + // [TARGET nackAsync(String, String, String...)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message1_ack_id"] + // [VARIABLE "message2_ack_id"] + public void nackMoreMessagesAsync(String subscriptionName, String ackId1, String ackId2) + throws ExecutionException, InterruptedException { + // [START nackMoreMessagesAsync] + Future future = pubsub.nackAsync(subscriptionName, ackId1, ackId2); + // ... + future.get(); + // [END nackMoreMessagesAsync] + } + + /** + * Example of nacking a list of messages. + */ + // [TARGET nack(String, Iterable)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message1_ack_id"] + // [VARIABLE "message2_ack_id"] + public void nackMessageList(String subscriptionName, String ackId1, String ackId2) { + // [START nackMessageList] + List ackIds = new LinkedList<>(); + ackIds.add(ackId1); + ackIds.add(ackId2); + pubsub.nack(subscriptionName, ackIds); + // [END nackMessageList] + } + + /** + * Example of asynchronously nacking a list of messages. + */ + // [TARGET nackAsync(String, Iterable)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message1_ack_id"] + // [VARIABLE "message2_ack_id"] + public void nackMessageListAsync(String subscriptionName, String ackId1, String ackId2) + throws ExecutionException, InterruptedException { + // [START nackMessageListAsync] + List ackIds = new LinkedList<>(); + ackIds.add(ackId1); + ackIds.add(ackId2); + Future future = pubsub.nackAsync(subscriptionName, ackIds); + // ... + future.get(); + // [END nackMessageListAsync] + } + + /** + * Example of modifying the ack deadline of one message. + */ + // [TARGET modifyAckDeadline(String, int, TimeUnit, String, String...)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message_ack_id"] + public void modifyAckDeadlineOneMessage(String subscriptionName, String ackId) { + // [START modifyAckDeadlineOneMessage] + pubsub.modifyAckDeadline(subscriptionName, 60, TimeUnit.SECONDS, ackId); + // [END modifyAckDeadlineOneMessage] + } + + /** + * Example of asynchronously modifying the ack deadline of one message. + */ + // [TARGET modifyAckDeadlineAsync(String, int, TimeUnit, String, String...)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message_ack_id"] + public void modifyAckDeadlineOneMessageAsync(String subscriptionName, String ackId) + throws ExecutionException, InterruptedException { + // [START modifyAckDeadlineOneMessageAsync] + Future future = + pubsub.modifyAckDeadlineAsync(subscriptionName, 60, TimeUnit.SECONDS, ackId); + // ... + future.get(); + // [END modifyAckDeadlineOneMessageAsync] + } + + /** + * Example of modifying the ack deadline of some messages. + */ + // [TARGET modifyAckDeadline(String, int, TimeUnit, String, String...)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message1_ack_id"] + // [VARIABLE "message2_ack_id"] + public void modifyAckDeadlineMoreMessages(String subscriptionName, String ackId1, String ackId2) { + // [START modifyAckDeadline] + pubsub.modifyAckDeadline(subscriptionName, 60, TimeUnit.SECONDS, ackId1, ackId2); + // [END modifyAckDeadline] + } + + /** + * Example of asynchronously modifying the ack deadline of some messages. + */ + // [TARGET modifyAckDeadlineAsync(String, int, TimeUnit, String, String...)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message1_ack_id"] + // [VARIABLE "message2_ack_id"] + public void modifyAckDeadlineMoreMessagesAsync(String subscriptionName, String ackId1, + String ackId2) throws ExecutionException, InterruptedException { + // [START modifyAckDeadlineMoreMessagesAsync] + Future future = + pubsub.modifyAckDeadlineAsync(subscriptionName, 60, TimeUnit.SECONDS, ackId1, ackId2); + // ... + future.get(); + // [END modifyAckDeadlineMoreMessagesAsync] + } + + /** + * Example of modifying the ack deadline of a list of messages. + */ + // [TARGET modifyAckDeadline(String, int, TimeUnit, Iterable)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message1_ack_id"] + // [VARIABLE "message2_ack_id"] + public void modifyAckDeadlineMessageList(String subscriptionName, String ackId1, String ackId2) { + // [START modifyAckDeadlineMessageList] + List ackIds = new LinkedList<>(); + ackIds.add(ackId1); + ackIds.add(ackId2); + pubsub.modifyAckDeadline(subscriptionName, 60, TimeUnit.SECONDS, ackIds); + // [END modifyAckDeadlineMessageList] + } + + /** + * Example of asynchronously modifying the ack deadline of a list of messages. + */ + // [TARGET modifyAckDeadlineAsync(String, int, TimeUnit, Iterable)] + // [VARIABLE "my_subscription_name"] + // [VARIABLE "message1_ack_id"] + // [VARIABLE "message2_ack_id"] + public void modifyAckDeadlineMessageListAsync(String subscriptionName, String ackId1, + String ackId2) throws ExecutionException, InterruptedException { + // [START modifyAckDeadlineMessageListAsync] + List ackIds = new LinkedList<>(); + ackIds.add(ackId1); + ackIds.add(ackId2); + Future future = + pubsub.modifyAckDeadlineAsync(subscriptionName, 60, TimeUnit.SECONDS, ackIds); + // ... + future.get(); + // [END modifyAckDeadlineMessageListAsync] + } + + /** + * Example of getting a topic policy. + */ + // [TARGET getTopicPolicy(String)] + // [VARIABLE "my_topic_name"] + public Policy getTopicPolicy(String topicName) { + // [START getTopicPolicy] + Policy policy = pubsub.getTopicPolicy(topicName); + if (policy == null) { + // topic was not found + } + // [END getTopicPolicy] + return policy; + } + + /** + * Example of asynchronously getting a topic policy. + */ + // [TARGET getTopicPolicyAsync(String)] + // [VARIABLE "my_topic_name"] + public Policy getTopicPolicyAsync(String topicName) + throws ExecutionException, InterruptedException { + // [START getTopicPolicyAsync] + Future future = pubsub.getTopicPolicyAsync(topicName); + // ... + Policy policy = future.get(); + if (policy == null) { + // topic was not found + } + // [END getTopicPolicyAsync] + return policy; + } + + /** + * Example of replacing a topic policy. + */ + // [TARGET replaceTopicPolicy(String, Policy)] + // [VARIABLE "my_topic_name"] + public Policy replaceTopicPolicy(String topicName) { + // [START replaceTopicPolicy] + Policy policy = pubsub.getTopicPolicy(topicName); + Policy updatedPolicy = policy.toBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build(); + updatedPolicy = pubsub.replaceTopicPolicy(topicName, updatedPolicy); + // [END replaceTopicPolicy] + return updatedPolicy; + } + + /** + * Example of asynchronously replacing a topic policy. + */ + // [TARGET replaceTopicPolicyAsync(String, Policy)] + // [VARIABLE "my_topic_name"] + public Policy replaceTopicPolicyAsync(String topicName) + throws ExecutionException, InterruptedException { + // [START replaceTopicPolicyAsync] + Policy policy = pubsub.getTopicPolicy(topicName); + Policy updatedPolicy = policy.toBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build(); + Future future = pubsub.replaceTopicPolicyAsync(topicName, updatedPolicy); + // ... + updatedPolicy = future.get(); + // [END replaceTopicPolicyAsync] + return updatedPolicy; + } + + /** + * Example of testing whether the caller has the provided permissions on a topic. + */ + // [TARGET testTopicPermissions(String, List)] + // [VARIABLE "my_topic_name"] + public List testTopicPermissions(String topicName) { + // [START testTopicPermissions] + List permissions = new LinkedList<>(); + permissions.add("pubsub.topics.get"); + List testedPermissions = pubsub.testTopicPermissions(topicName, permissions); + // [END testTopicPermissions] + return testedPermissions; + } + + /** + * Example of asynchronously testing whether the caller has the provided permissions on a topic. + */ + // [TARGET testTopicPermissionsAsync(String, List)] + // [VARIABLE "my_topic_name"] + public List testTopicPermissionsAsync(String topicName) + throws ExecutionException, InterruptedException { + // [START testTopicPermissionsAsync] + List permissions = new LinkedList<>(); + permissions.add("pubsub.topics.get"); + Future> future = pubsub.testTopicPermissionsAsync(topicName, permissions); + // ... + List testedPermissions = future.get(); + // [END testTopicPermissionsAsync] + return testedPermissions; + } + + /** + * Example of getting a subscription policy. + */ + // [TARGET getSubscriptionPolicy(String)] + // [VARIABLE "my_subscription_name"] + public Policy getSubscriptionPolicy(String subscriptionName) { + // [START getSubscriptionPolicy] + Policy policy = pubsub.getSubscriptionPolicy(subscriptionName); + if (policy == null) { + // subscription was not found + } + // [END getSubscriptionPolicy] + return policy; + } + + /** + * Example of asynchronously getting a subscription policy. + */ + // [TARGET getSubscriptionPolicyAsync(String)] + // [VARIABLE "my_subscription_name"] + public Policy getSubscriptionPolicyAsync(String subscriptionName) + throws ExecutionException, InterruptedException { + // [START getSubscriptionPolicyAsync] + Future future = pubsub.getSubscriptionPolicyAsync(subscriptionName); + // ... + Policy policy = future.get(); + if (policy == null) { + // subscription was not found + } + // [END getSubscriptionPolicyAsync] + return policy; + } + + /** + * Example of replacing a subscription policy. + */ + // [TARGET replaceSubscriptionPolicy(String, Policy)] + // [VARIABLE "my_subscription_name"] + public Policy replaceSubscriptionPolicy(String subscriptionName) { + // [START replaceSubscriptionPolicy] + Policy policy = pubsub.getSubscriptionPolicy(subscriptionName); + Policy updatedPolicy = policy.toBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build(); + updatedPolicy = pubsub.replaceSubscriptionPolicy(subscriptionName, updatedPolicy); + // [END replaceSubscriptionPolicy] + return updatedPolicy; + } + + /** + * Example of asynchronously replacing a subscription policy. + */ + // [TARGET replaceSubscriptionPolicyAsync(String, Policy)] + // [VARIABLE "my_subscription_name"] + public Policy replaceSubscriptionPolicyAsync(String subscriptionName) + throws ExecutionException, InterruptedException { + // [START replaceSubscriptionPolicyAsync] + Policy policy = pubsub.getSubscriptionPolicy(subscriptionName); + Policy updatedPolicy = policy.toBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build(); + Future future = + pubsub.replaceSubscriptionPolicyAsync(subscriptionName, updatedPolicy); + // ... + updatedPolicy = future.get(); + // [END replaceSubscriptionPolicyAsync] + return updatedPolicy; + } + + /** + * Example of testing whether the caller has the provided permissions on a subscription. + */ + // [TARGET testSubscriptionPermissions(String, List)] + // [VARIABLE "my_subscription_name"] + public List testSubscriptionPermissions(String subscriptionName) { + // [START testSubscriptionPermissions] + List permissions = new LinkedList<>(); + permissions.add("pubsub.subscriptions.get"); + List testedPermissions = + pubsub.testSubscriptionPermissions(subscriptionName, permissions); + // [END testSubscriptionPermissions] + return testedPermissions; + } + + /** + * Example of asynchronously testing whether the caller has the provided permissions on a + * subscription. + */ + // [TARGET testSubscriptionPermissionsAsync(String, List)] + // [VARIABLE "my_subscription_name"] + public List testSubscriptionPermissionsAsync(String subscriptionName) + throws ExecutionException, InterruptedException { + // [START testSubscriptionPermissionsAsync] + List permissions = new LinkedList<>(); + permissions.add("pubsub.subscriptions.get"); + Future> future = + pubsub.testSubscriptionPermissionsAsync(subscriptionName, permissions); + // ... + List testedPermissions = future.get(); + // [END testSubscriptionPermissionsAsync] + return testedPermissions; + } +} diff --git a/google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/snippets/SubscriptionSnippets.java b/google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/snippets/SubscriptionSnippets.java new file mode 100644 index 000000000000..3e0ac0189fbb --- /dev/null +++ b/google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/snippets/SubscriptionSnippets.java @@ -0,0 +1,316 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * EDITING INSTRUCTIONS + * This file is referenced in Subscription's javadoc. Any change to this file should be reflected in + * Subscription's javadoc. + */ + +package com.google.cloud.examples.pubsub.snippets; + +import com.google.cloud.Identity; +import com.google.cloud.Policy; +import com.google.cloud.Role; +import com.google.cloud.pubsub.Message; +import com.google.cloud.pubsub.PubSub.MessageConsumer; +import com.google.cloud.pubsub.PubSub.MessageProcessor; +import com.google.cloud.pubsub.PushConfig; +import com.google.cloud.pubsub.ReceivedMessage; +import com.google.cloud.pubsub.Subscription; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * This class contains a number of snippets for the {@link Subscription} class. + */ +public class SubscriptionSnippets { + + private final Subscription subscription; + + public SubscriptionSnippets(Subscription subscription) { + this.subscription = subscription; + } + + /** + * Example of getting the subscription's latest information. + */ + // [TARGET reload()] + public Subscription reload() { + // [START reload] + Subscription latestSubscription = subscription.reload(); + if (latestSubscription == null) { + // the subscription was not found + } + // [END reload] + return latestSubscription; + } + + /** + * Example of asynchronously getting the subscription's latest information. + */ + // [TARGET reloadAsync()] + public Subscription reloadAsync() throws ExecutionException, InterruptedException { + // [START reloadAsync] + Future future = subscription.reloadAsync(); + // ... + Subscription latestSubscription = future.get(); + if (latestSubscription == null) { + // the subscription was not found + } + // [END reloadAsync] + return latestSubscription; + } + + /** + * Example of deleting the subscription. + */ + // [TARGET delete()] + public boolean delete() { + // [START delete] + boolean deleted = subscription.delete(); + if (deleted) { + // the subscription was deleted + } else { + // the subscription was not found + } + // [END delete] + return deleted; + } + + /** + * Example of asynchronously deleting the subscription. + */ + // [TARGET deleteAsync()] + public boolean deleteAsync() throws ExecutionException, InterruptedException { + // [START deleteAsync] + Future future = subscription.deleteAsync(); + // ... + boolean deleted = future.get(); + if (deleted) { + // the subscription was deleted + } else { + // the subscription was not found + } + // [END deleteAsync] + return deleted; + } + + /** + * Example of replacing the push configuration of the subscription, setting the push endpoint. + */ + // [TARGET replacePushConfig(PushConfig)] + // [VARIABLE "https://www.example.com/push"] + public void replacePushConfig(String endpoint) { + // [START replacePushConfig] + PushConfig pushConfig = PushConfig.of(endpoint); + subscription.replacePushConfig(pushConfig); + // [END replacePushConfig] + } + + /** + * Example of replacing the push configuration of the subscription, making it a pull + * subscription. + */ + // [TARGET replacePushConfig(PushConfig)] + public void replacePushConfigToPull() { + // [START replacePushConfigToPull] + subscription.replacePushConfig(null); + // [END replacePushConfigToPull] + } + + /** + * Example of asynchronously replacing the push configuration of the subscription, setting the + * push endpoint. + */ + // [TARGET replacePushConfigAsync(PushConfig)] + // [VARIABLE "https://www.example.com/push"] + public void replacePushConfigAsync(String endpoint) + throws ExecutionException, InterruptedException { + // [START replacePushConfigAsync] + PushConfig pushConfig = PushConfig.of(endpoint); + Future future = subscription.replacePushConfigAsync(pushConfig); + // ... + future.get(); + // [END replacePushConfigAsync] + } + + /** + * Example of asynchronously replacing the push configuration of the subscription, making it a + * pull subscription. + */ + // [TARGET replacePushConfigAsync(PushConfig)] + public void replacePushConfigToPullAsync() + throws ExecutionException, InterruptedException { + // [START replacePushConfigToPullAsync] + Future future = subscription.replacePushConfigAsync(null); + // ... + future.get(); + // [END replacePushConfigToPullAsync] + } + + /** + * Example of pulling a maximum number of messages from the subscription. + */ + // [TARGET pull(int)] + public void pull() { + // [START pull] + Iterator messages = subscription.pull(100); + // Ack deadline is renewed until the message is consumed + while (messages.hasNext()) { + ReceivedMessage message = messages.next(); + // do something with message and ack/nack it + message.ack(); // or message.nack() + } + // [END pull] + } + + /** + * Example of asynchronously pulling a maximum number of messages from the subscription. + */ + // [TARGET pullAsync(int)] + public void pullAsync() throws ExecutionException, InterruptedException { + // [START pullAsync] + Future> future = subscription.pullAsync(100); + // ... + Iterator messages = future.get(); + // Ack deadline is renewed until the message is consumed + while (messages.hasNext()) { + ReceivedMessage message = messages.next(); + // do something with message and ack/nack it + message.ack(); // or message.nack() + } + // [END pullAsync] + } + + /** + * Example of continuously pulling messages from the subscription. + */ + // [TARGET pullAsync(MessageProcessor, PullOption...)] + // [VARIABLE "my_subscription_name"] + public void pullWithMessageConsumer(String subscriptionName) throws Exception { + // [START pullWithMessageConsumer] + MessageProcessor callback = new MessageProcessor() { + public void process(Message message) throws Exception { + // Ack deadline is renewed until this method returns + // Message is acked if this method returns successfully + // Message is nacked if this method throws an exception + } + }; + MessageConsumer consumer = subscription.pullAsync(callback); + // ... + // Stop pulling + consumer.close(); + // [END pullWithMessageConsumer] + } + + /** + * Example of getting the subscription's policy. + */ + // [TARGET getPolicy()] + public Policy getPolicy() { + // [START getPolicy] + Policy policy = subscription.getPolicy(); + if (policy == null) { + // subscription was not found + } + // [END getPolicy] + return policy; + } + + /** + * Example of asynchronously getting the subscription's policy. + */ + // [TARGET getPolicyAsync()] + public Policy getPolicyAsync() throws ExecutionException, InterruptedException { + // [START getPolicyAsync] + Future future = subscription.getPolicyAsync(); + // ... + Policy policy = future.get(); + if (policy == null) { + // subscription was not found + } + // [END getPolicyAsync] + return policy; + } + + /** + * Example of replacing the subscription's policy. + */ + // [TARGET replacePolicy(Policy)] + public Policy replacePolicy() { + // [START replacePolicy] + Policy policy = subscription.getPolicy(); + Policy updatedPolicy = policy.toBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build(); + updatedPolicy = subscription.replacePolicy(updatedPolicy); + // [END replacePolicy] + return updatedPolicy; + } + + /** + * Example of asynchronously replacing the subscription's policy. + */ + // [TARGET replacePolicyAsync(Policy)] + public Policy replacePolicyAsync() + throws ExecutionException, InterruptedException { + // [START replacePolicyAsync] + Policy policy = subscription.getPolicy(); + Policy updatedPolicy = policy.toBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build(); + Future future = subscription.replacePolicyAsync(updatedPolicy); + // ... + updatedPolicy = future.get(); + // [END replacePolicyAsync] + return updatedPolicy; + } + + /** + * Example of testing whether the caller has the provided permissions on the subscription. + */ + // [TARGET testPermissions(List)] + public List testPermissions() { + // [START testPermissions] + List permissions = new LinkedList<>(); + permissions.add("pubsub.subscriptions.get"); + List testedPermissions = subscription.testPermissions(permissions); + // [END testPermissions] + return testedPermissions; + } + + /** + * Example of asynchronously testing whether the caller has the provided permissions on the + * subscription. + */ + // [TARGET testPermissionsAsync(List)] + public List testPermissionsAsync() + throws ExecutionException, InterruptedException { + // [START testPermissionsAsync] + List permissions = new LinkedList<>(); + permissions.add("pubsub.subscriptions.get"); + Future> future = subscription.testPermissionsAsync(permissions); + // ... + List testedPermissions = future.get(); + // [END testPermissionsAsync] + return testedPermissions; + } +} diff --git a/google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/snippets/TopicSnippets.java b/google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/snippets/TopicSnippets.java new file mode 100644 index 000000000000..368481266a5f --- /dev/null +++ b/google-cloud-examples/src/main/java/com/google/cloud/examples/pubsub/snippets/TopicSnippets.java @@ -0,0 +1,332 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * EDITING INSTRUCTIONS + * This file is referenced in Topic's javadoc. Any change to this file should be reflected in + * Topic's javadoc. + */ + +package com.google.cloud.examples.pubsub.snippets; + +import com.google.cloud.AsyncPage; +import com.google.cloud.Identity; +import com.google.cloud.Page; +import com.google.cloud.Policy; +import com.google.cloud.Role; +import com.google.cloud.pubsub.Message; +import com.google.cloud.pubsub.PubSub.ListOption; +import com.google.cloud.pubsub.SubscriptionId; +import com.google.cloud.pubsub.Topic; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * This class contains a number of snippets for the {@link Topic} class. + */ +public class TopicSnippets { + + private final Topic topic; + + public TopicSnippets(Topic topic) { + this.topic = topic; + } + + /** + * Example of getting the topic's latest information. + */ + // [TARGET reload()] + public Topic reload() { + // [START reload] + Topic latestTopic = topic.reload(); + if (latestTopic == null) { + // the topic was not found + } + // [END reload] + return latestTopic; + } + + /** + * Example of asynchronously getting the topic's latest information. + */ + // [TARGET reloadAsync()] + public Topic reloadAsync() throws ExecutionException, InterruptedException { + // [START reloadAsync] + Future future = topic.reloadAsync(); + // ... + Topic latestTopic = future.get(); + if (latestTopic == null) { + // the topic was not found + } + // [END reloadAsync] + return latestTopic; + } + + /** + * Example of deleting the topic. + */ + // [TARGET delete()] + public boolean delete() { + // [START delete] + boolean deleted = topic.delete(); + if (deleted) { + // the topic was deleted + } else { + // the topic was not found + } + // [END delete] + return deleted; + } + + /** + * Example of asynchronously deleting the topic. + */ + // [TARGET deleteAsync()] + public boolean deleteAsync() throws ExecutionException, InterruptedException { + // [START deleteAsync] + Future future = topic.deleteAsync(); + // ... + boolean deleted = future.get(); + if (deleted) { + // the topic was deleted + } else { + // the topic was not found + } + // [END deleteAsync] + return deleted; + } + + /** + * Example of publishing one message to the topic. + */ + // [TARGET publish(Message)] + public String publishOneMessage() { + // [START publishOneMessage] + Message message = Message.of("payload"); + String messageId = topic.publish(message); + // [END publishOneMessage] + return messageId; + } + + /** + * Example of asynchronously publishing one message to the topic. + */ + // [TARGET publishAsync(Message)] + public String publishOneMessageAsync() + throws ExecutionException, InterruptedException { + // [START publishOneMessageAsync] + Message message = Message.of("payload"); + Future future = topic.publishAsync(message); + // ... + String messageId = future.get(); + // [END publishOneMessageAsync] + return messageId; + } + + + /** + * Example of publishing a list of messages to the topic. + */ + // [TARGET publish(Iterable)] + public List publishMessageList() { + // [START publishMessageList] + List messages = new LinkedList<>(); + messages.add(Message.of("payload1")); + messages.add(Message.of("payload2")); + List messageIds = topic.publish(messages); + // [END publishMessageList] + return messageIds; + } + + /** + * Example of asynchronously publishing a list of messages to the topic. + */ + // [TARGET publishAsync(Iterable)] + public List publishMessageListAsync() + throws ExecutionException, InterruptedException { + // [START publishMessageListAsync] + List messages = new LinkedList<>(); + messages.add(Message.of("payload1")); + messages.add(Message.of("payload2")); + Future> future = topic.publishAsync(messages); + // ... + List messageIds = future.get(); + // [END publishMessageListAsync] + return messageIds; + } + + /** + * Example of publishing some messages to the topic. + */ + // [TARGET publish(Message, Message...)] + public List publishMessages() { + // [START publishMessages] + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + List messageIds = topic.publish(message1, message2); + // [END publishMessages] + return messageIds; + } + + /** + * Example of asynchronously publishing some messages to the topic. + */ + // [TARGET publishAsync(Message, Message...)] + public List publishMessagesAsync() + throws ExecutionException, InterruptedException { + // [START publishMessagesAsync] + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + Future> future = topic.publishAsync(message1, message2); + // ... + List messageIds = future.get(); + // [END publishMessagesAsync] + return messageIds; + } + + /** + * Example of listing subscriptions for the topic, specifying the page size. + */ + // [TARGET listSubscriptions(ListOption...)] + public Page listSubscriptionsForTopic() { + // [START listSubscriptionsForTopic] + Page subscriptions = topic.listSubscriptions(ListOption.pageSize(100)); + Iterator subscriptionIterator = subscriptions.iterateAll(); + while (subscriptionIterator.hasNext()) { + SubscriptionId subscription = subscriptionIterator.next(); + // do something with the subscription identity + } + // [END listSubscriptionsForTopic] + return subscriptions; + } + + /** + * Example of asynchronously listing subscriptions for the topic, specifying the page size. + */ + // [TARGET listSubscriptionsAsync(ListOption...)] + public Page listSubscriptionsForTopicAsync() + throws ExecutionException, InterruptedException { + // [START listSubscriptionsForTopicAsync] + Future> future = + topic.listSubscriptionsAsync(ListOption.pageSize(100)); + // ... + AsyncPage subscriptions = future.get(); + Iterator subscriptionIterator = subscriptions.iterateAll(); + while (subscriptionIterator.hasNext()) { + SubscriptionId subscription = subscriptionIterator.next(); + // do something with the subscription identity + } + // [END listSubscriptionsForTopicAsync] + return subscriptions; + } + + /** + * Example of getting the topic's policy. + */ + // [TARGET getPolicy()] + public Policy getPolicy() { + // [START getPolicy] + Policy policy = topic.getPolicy(); + if (policy == null) { + // topic was not found + } + // [END getPolicy] + return policy; + } + + /** + * Example of asynchronously getting the topic's policy. + */ + // [TARGET getPolicyAsync()] + public Policy getPolicyAsync() throws ExecutionException, InterruptedException { + // [START getPolicyAsync] + Future future = topic.getPolicyAsync(); + // ... + Policy policy = future.get(); + if (policy == null) { + // topic was not found + } + // [END getPolicyAsync] + return policy; + } + + /** + * Example of replacing the topic's policy. + */ + // [TARGET replacePolicy(Policy)] + public Policy replacePolicy() { + // [START replacePolicy] + Policy policy = topic.getPolicy(); + Policy updatedPolicy = policy.toBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build(); + updatedPolicy = topic.replacePolicy(updatedPolicy); + // [END replacePolicy] + return updatedPolicy; + } + + /** + * Example of asynchronously replacing the topic's policy. + */ + // [TARGET replacePolicyAsync(Policy)] + public Policy replacePolicyAsync() + throws ExecutionException, InterruptedException { + // [START replacePolicyAsync] + Policy policy = topic.getPolicy(); + Policy updatedPolicy = policy.toBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build(); + Future future = topic.replacePolicyAsync(updatedPolicy); + // ... + updatedPolicy = future.get(); + // [END replacePolicyAsync] + return updatedPolicy; + } + + /** + * Example of testing whether the caller has the provided permissions on the topic. + */ + // [TARGET testPermissions(List)] + public List testPermissions() { + // [START testPermissions] + List permissions = new LinkedList<>(); + permissions.add("pubsub.topics.get"); + List testedPermissions = topic.testPermissions(permissions); + // [END testPermissions] + return testedPermissions; + } + + /** + * Example of asynchronously testing whether the caller has the provided permissions on the + * topic. + */ + // [TARGET testPermissionsAsync(List)] + public List testPermissionsAsync() + throws ExecutionException, InterruptedException { + // [START testPermissionsAsync] + List permissions = new LinkedList<>(); + permissions.add("pubsub.topics.get"); + Future> future = topic.testPermissionsAsync(permissions); + // ... + List testedPermissions = future.get(); + // [END testPermissionsAsync] + return testedPermissions; + } +} diff --git a/google-cloud-examples/src/test/java/com/google/cloud/examples/pubsub/snippets/ITPubSubSnippets.java b/google-cloud-examples/src/test/java/com/google/cloud/examples/pubsub/snippets/ITPubSubSnippets.java new file mode 100644 index 000000000000..81e17dd5d8ba --- /dev/null +++ b/google-cloud-examples/src/test/java/com/google/cloud/examples/pubsub/snippets/ITPubSubSnippets.java @@ -0,0 +1,269 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.examples.pubsub.snippets; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.Identity; +import com.google.cloud.Page; +import com.google.cloud.Policy; +import com.google.cloud.Role; +import com.google.cloud.pubsub.PubSub; +import com.google.cloud.pubsub.PubSubOptions; +import com.google.cloud.pubsub.ReceivedMessage; +import com.google.cloud.pubsub.Subscription; +import com.google.cloud.pubsub.SubscriptionId; +import com.google.cloud.pubsub.SubscriptionInfo; +import com.google.cloud.pubsub.Topic; +import com.google.cloud.pubsub.TopicInfo; +import com.google.common.collect.Iterators; +import com.google.common.collect.Sets; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +public class ITPubSubSnippets { + + private static final String NAME_SUFFIX = UUID.randomUUID().toString(); + + private static PubSub pubsub; + private static PubSubSnippets pubsubSnippets; + + @Rule + public Timeout globalTimeout = Timeout.seconds(300); + + private static String formatForTest(String resourceName) { + return resourceName + "-" + NAME_SUFFIX; + } + + @BeforeClass + public static void beforeClass() { + pubsub = PubSubOptions.getDefaultInstance().getService(); + pubsubSnippets = new PubSubSnippets(pubsub); + } + + @AfterClass + public static void afterClass() throws Exception { + if (pubsub != null) { + pubsub.close(); + } + } + + @Test + public void testTopicAndSubscription() throws ExecutionException, InterruptedException { + String topicName1 = formatForTest("topic-name1"); + String topicName2 = formatForTest("topic-name2"); + Topic topic1 = pubsubSnippets.createTopic(topicName1); + Topic topic2 = pubsubSnippets.createTopicAsync(topicName2); + assertNotNull(topic1); + assertNotNull(topic2); + topic1 = pubsubSnippets.getTopic(topicName1); + topic2 = pubsubSnippets.getTopicAsync(topicName2); + assertNotNull(topic1); + assertNotNull(topic2); + Set topics = Sets.newHashSet(pubsubSnippets.listTopics().iterateAll()); + while (!topics.contains(topic1) || !topics.contains(topic2)) { + Thread.sleep(500); + topics = Sets.newHashSet(pubsubSnippets.listTopics().iterateAll()); + } + topics = Sets.newHashSet(pubsubSnippets.listTopicsAsync().iterateAll()); + while (!topics.contains(topic1) || !topics.contains(topic2)) { + Thread.sleep(500); + topics = Sets.newHashSet(pubsubSnippets.listTopicsAsync().iterateAll()); + } + String subscriptionName1 = formatForTest("subscription-name1"); + String subscriptionName2 = formatForTest("subscription-name2"); + Subscription subscription1 = + pubsubSnippets.createSubscription(topicName1, subscriptionName1); + Subscription subscription2 = + pubsubSnippets.createSubscriptionAsync(topicName2, subscriptionName2); + assertNotNull(subscription1); + assertNotNull(subscription2); + Page page = pubsubSnippets.listSubscriptionsForTopic(topicName1); + while (Iterators.size(page.iterateAll()) < 1) { + page = pubsubSnippets.listSubscriptionsForTopic(topicName1); + } + assertEquals(subscriptionName1, page.iterateAll().next().getSubscription()); + page = pubsubSnippets.listSubscriptionsForTopicAsync(topicName2); + while (Iterators.size(page.iterateAll()) < 1) { + page = pubsubSnippets.listSubscriptionsForTopicAsync(topicName2); + } + assertEquals(subscriptionName2, page.iterateAll().next().getSubscription()); + String endpoint = "https://" + pubsub.getOptions().getProjectId() + ".appspot.com/push"; + pubsubSnippets.replacePushConfig(subscriptionName1, endpoint); + pubsubSnippets.replacePushConfigAsync(subscriptionName2, endpoint); + subscription1 = pubsubSnippets.getSubscription(subscriptionName1); + subscription2 = pubsubSnippets.getSubscriptionAsync(subscriptionName2); + assertEquals(endpoint, subscription1.getPushConfig().getEndpoint()); + assertEquals(endpoint, subscription2.getPushConfig().getEndpoint()); + pubsubSnippets.replacePushConfigToPull(subscriptionName1); + pubsubSnippets.replacePushConfigToPullAsync(subscriptionName2); + subscription1 = pubsubSnippets.getSubscription(subscriptionName1); + subscription2 = pubsubSnippets.getSubscriptionAsync(subscriptionName2); + assertNull(subscription1.getPushConfig()); + assertNull(subscription2.getPushConfig()); + assertTrue(pubsubSnippets.deleteTopic(topicName1)); + assertTrue(pubsubSnippets.deleteTopicAsync(topicName2)); + assertTrue(pubsubSnippets.deleteSubscription(subscriptionName1)); + assertTrue(pubsubSnippets.deleteSubscriptionAsync(subscriptionName2)); + } + + @Test + public void testPublishAndPullMessage() throws Exception { + String topicName = formatForTest("topic-name"); + String subscriptionName = formatForTest("subscription-name"); + pubsub.create(TopicInfo.of(topicName)); + pubsub.create(SubscriptionInfo.of(topicName, subscriptionName)); + assertNotNull(pubsubSnippets.publishOneMessage(topicName)); + pubsubSnippets.pull(subscriptionName); + assertNotNull(pubsubSnippets.publishOneMessage(topicName)); + assertEquals(2, pubsubSnippets.publishMessages(topicName).size()); + assertEquals(2, pubsubSnippets.publishMessageList(topicName).size()); + Set messages = new HashSet<>(); + while (messages.size() < 5) { + Iterators.addAll(messages, pubsub.pull(subscriptionName, 100)); + } + Iterator messageIterator = messages.iterator(); + pubsubSnippets.modifyAckDeadlineOneMessage(subscriptionName, messageIterator.next().getAckId()); + pubsubSnippets.modifyAckDeadlineMoreMessages(subscriptionName, + messageIterator.next().getAckId(), messageIterator.next().getAckId()); + pubsubSnippets.modifyAckDeadlineMessageList(subscriptionName, messageIterator.next().getAckId(), + messageIterator.next().getAckId()); + messageIterator = messages.iterator(); + pubsubSnippets.nackOneMessage(subscriptionName, messageIterator.next().getAckId()); + pubsubSnippets.nackMoreMessages(subscriptionName, messageIterator.next().getAckId(), + messageIterator.next().getAckId()); + pubsubSnippets.nackMessageList(subscriptionName, messageIterator.next().getAckId(), + messageIterator.next().getAckId()); + messages.clear(); + while (messages.size() < 5) { + Iterators.addAll(messages, pubsub.pull(subscriptionName, 100)); + } + messageIterator = messages.iterator(); + pubsubSnippets.ackOneMessage(subscriptionName, messageIterator.next().getAckId()); + pubsubSnippets.ackMoreMessages(subscriptionName, messageIterator.next().getAckId(), + messageIterator.next().getAckId()); + pubsubSnippets.ackMessageList(subscriptionName, messageIterator.next().getAckId(), + messageIterator.next().getAckId()); + assertTrue(pubsubSnippets.deleteTopic(topicName)); + assertTrue(pubsubSnippets.deleteSubscription(subscriptionName)); + } + + @Test + public void testPublishAndPullMessageAsync() throws Exception { + String topicName = formatForTest("topic-name-async"); + String subscriptionName = formatForTest("subscription-name-async"); + pubsub.create(TopicInfo.of(topicName)); + pubsub.create(SubscriptionInfo.of(topicName, subscriptionName)); + pubsubSnippets.pullWithMessageConsumer(subscriptionName); + assertNotNull(pubsubSnippets.publishOneMessageAsync(topicName)); + pubsubSnippets.pullAsync(subscriptionName); + assertNotNull(pubsubSnippets.publishOneMessageAsync(topicName)); + assertEquals(2, pubsubSnippets.publishMessagesAsync(topicName).size()); + assertEquals(2, pubsubSnippets.publishMessageListAsync(topicName).size()); + Set messages = new HashSet<>(); + while (messages.size() < 5) { + Iterators.addAll(messages, pubsub.pull(subscriptionName, 100)); + } + Iterator messageIterator = messages.iterator(); + pubsubSnippets.modifyAckDeadlineOneMessageAsync(subscriptionName, + messageIterator.next().getAckId()); + pubsubSnippets.modifyAckDeadlineMoreMessagesAsync(subscriptionName, + messageIterator.next().getAckId(), messageIterator.next().getAckId()); + pubsubSnippets.modifyAckDeadlineMessageListAsync(subscriptionName, + messageIterator.next().getAckId(), messageIterator.next().getAckId()); + messageIterator = messages.iterator(); + pubsubSnippets.nackOneMessageAsync(subscriptionName, messageIterator.next().getAckId()); + pubsubSnippets.nackMoreMessagesAsync(subscriptionName, messageIterator.next().getAckId(), + messageIterator.next().getAckId()); + pubsubSnippets.nackMessageListAsync(subscriptionName, messageIterator.next().getAckId(), + messageIterator.next().getAckId()); + messages.clear(); + while (messages.size() < 5) { + Iterators.addAll(messages, pubsub.pull(subscriptionName, 100)); + } + messageIterator = messages.iterator(); + pubsubSnippets.ackOneMessageAsync(subscriptionName, messageIterator.next().getAckId()); + pubsubSnippets.ackMoreMessagesAsync(subscriptionName, messageIterator.next().getAckId(), + messageIterator.next().getAckId()); + pubsubSnippets.ackMessageListAsync(subscriptionName, messageIterator.next().getAckId(), + messageIterator.next().getAckId()); + assertTrue(pubsubSnippets.deleteTopicAsync(topicName)); + assertTrue(pubsubSnippets.deleteSubscriptionAsync(subscriptionName)); + } + + @Test + public void testTopicSubscriptionPolicy() { + String topicName = formatForTest("test-topic-policy"); + Topic topic = pubsubSnippets.createTopic(topicName); + Policy policy = pubsubSnippets.getTopicPolicy(topicName); + assertNotNull(policy); + policy = pubsubSnippets.replaceTopicPolicy(topicName); + assertTrue(policy.getBindings().containsKey(Role.viewer())); + assertTrue(policy.getBindings().get(Role.viewer()).contains(Identity.allAuthenticatedUsers())); + List permissions = pubsubSnippets.testTopicPermissions(topicName); + assertTrue(permissions.get(0)); + String subscriptionName = formatForTest("test-subscription-policy"); + Subscription subscription = pubsubSnippets.createSubscription(topicName, subscriptionName); + policy = pubsubSnippets.getSubscriptionPolicy(subscriptionName); + assertNotNull(policy); + policy = pubsubSnippets.replaceSubscriptionPolicy(subscriptionName); + assertTrue(policy.getBindings().containsKey(Role.viewer())); + assertTrue(policy.getBindings().get(Role.viewer()).contains(Identity.allAuthenticatedUsers())); + permissions = pubsubSnippets.testSubscriptionPermissions(subscriptionName); + assertTrue(permissions.get(0)); + topic.delete(); + subscription.delete(); + } + + @Test + public void testTopicPolicyAsync() throws ExecutionException, InterruptedException { + String topicName = formatForTest("test-topic-policy-async"); + Topic topic = pubsubSnippets.createTopic(topicName); + Policy policy = pubsubSnippets.getTopicPolicyAsync(topicName); + assertNotNull(policy); + policy = pubsubSnippets.replaceTopicPolicyAsync(topicName); + assertTrue(policy.getBindings().containsKey(Role.viewer())); + assertTrue(policy.getBindings().get(Role.viewer()).contains(Identity.allAuthenticatedUsers())); + List permissions = pubsubSnippets.testTopicPermissionsAsync(topicName); + assertTrue(permissions.get(0)); + String subscriptionName = formatForTest("test-subscription-policy-async"); + Subscription subscription = pubsubSnippets.createSubscription(topicName, subscriptionName); + policy = pubsubSnippets.getSubscriptionPolicyAsync(subscriptionName); + assertNotNull(policy); + policy = pubsubSnippets.replaceSubscriptionPolicyAsync(subscriptionName); + assertTrue(policy.getBindings().containsKey(Role.viewer())); + assertTrue(policy.getBindings().get(Role.viewer()).contains(Identity.allAuthenticatedUsers())); + permissions = pubsubSnippets.testSubscriptionPermissionsAsync(subscriptionName); + assertTrue(permissions.get(0)); + topic.delete(); + subscription.delete(); + } +} diff --git a/google-cloud-examples/src/test/java/com/google/cloud/examples/pubsub/snippets/ITSubscriptionSnippets.java b/google-cloud-examples/src/test/java/com/google/cloud/examples/pubsub/snippets/ITSubscriptionSnippets.java new file mode 100644 index 000000000000..ffb912d9f549 --- /dev/null +++ b/google-cloud-examples/src/test/java/com/google/cloud/examples/pubsub/snippets/ITSubscriptionSnippets.java @@ -0,0 +1,125 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.examples.pubsub.snippets; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.Identity; +import com.google.cloud.Policy; +import com.google.cloud.Role; +import com.google.cloud.pubsub.Message; +import com.google.cloud.pubsub.PubSub; +import com.google.cloud.pubsub.PubSubOptions; +import com.google.cloud.pubsub.ReceivedMessage; +import com.google.cloud.pubsub.Subscription; +import com.google.cloud.pubsub.SubscriptionInfo; +import com.google.cloud.pubsub.Topic; +import com.google.cloud.pubsub.TopicInfo; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Iterator; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +public class ITSubscriptionSnippets { + + private static final String TOPIC = + "it-subscription-snippets-topic-" + UUID.randomUUID().toString(); + private static final String SUBSCRIPTION = + "it-subscription-snippets-subscription-" + UUID.randomUUID().toString(); + private static final Message MESSAGE1 = Message.of("message1"); + private static final Message MESSAGE2 = Message.of("message2"); + + private static PubSub pubsub; + private static Topic topic; + private static Subscription subscription; + + @BeforeClass + public static void beforeClass() { + pubsub = PubSubOptions.getDefaultInstance().getService(); + topic = pubsub.create(TopicInfo.of(TOPIC)); + subscription = pubsub.create(SubscriptionInfo.of(TOPIC, SUBSCRIPTION)); + } + + @AfterClass + public static void afterClass() throws Exception { + if (pubsub != null) { + topic.delete(); + subscription.delete(); + pubsub.close(); + } + } + + @Test + public void testPushConfig() throws ExecutionException, InterruptedException { + SubscriptionSnippets subscriptionSnippets = new SubscriptionSnippets(subscription); + String endpoint = "https://" + pubsub.getOptions().getProjectId() + ".appspot.com/push"; + subscriptionSnippets.replacePushConfig(endpoint); + Subscription updatedSubscription = pubsub.getSubscription(SUBSCRIPTION); + assertEquals(endpoint, updatedSubscription.getPushConfig().getEndpoint()); + subscriptionSnippets.replacePushConfigToPull(); + updatedSubscription = pubsub.getSubscription(SUBSCRIPTION); + assertNull(updatedSubscription.getPushConfig()); + subscriptionSnippets.replacePushConfigAsync(endpoint); + updatedSubscription = pubsub.getSubscription(SUBSCRIPTION); + assertEquals(endpoint, updatedSubscription.getPushConfig().getEndpoint()); + subscriptionSnippets.replacePushConfigToPullAsync(); + updatedSubscription = pubsub.getSubscription(SUBSCRIPTION); + assertNull(updatedSubscription.getPushConfig()); + } + + @Test + public void testPull() throws ExecutionException, InterruptedException { + SubscriptionSnippets subscriptionSnippets = new SubscriptionSnippets(subscription); + pubsub.publish(TOPIC, MESSAGE1, MESSAGE2); + subscriptionSnippets.pull(); + // messages have been acked, we should pull nothing + Iterator iterator = pubsub.pull(SUBSCRIPTION, 2); + assertFalse(iterator.hasNext()); + pubsub.publish(TOPIC, MESSAGE1, MESSAGE2); + subscriptionSnippets.pullAsync(); + // messages have been acked, we should pull nothing + iterator = pubsub.pull(SUBSCRIPTION, 2); + assertFalse(iterator.hasNext()); + subscriptionSnippets.pullAsync(); + } + + @Test + public void testPolicy() throws ExecutionException, InterruptedException { + SubscriptionSnippets subscriptionSnippets = new SubscriptionSnippets(subscription); + Policy policy = subscriptionSnippets.getPolicy(); + assertNotNull(policy); + assertEquals(policy, subscriptionSnippets.getPolicyAsync()); + policy = subscriptionSnippets.replacePolicy(); + assertTrue(policy.getBindings().get(Role.viewer()).contains(Identity.allAuthenticatedUsers())); + policy = subscription.replacePolicy(policy.toBuilder() + .removeIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build()); + assertFalse(policy.getBindings().containsKey(Role.viewer())); + policy = subscriptionSnippets.replacePolicyAsync(); + assertTrue(policy.getBindings().get(Role.viewer()).contains(Identity.allAuthenticatedUsers())); + assertTrue(subscriptionSnippets.delete()); + assertFalse(subscriptionSnippets.deleteAsync()); + } +} diff --git a/google-cloud-examples/src/test/java/com/google/cloud/examples/pubsub/snippets/ITTopicSnippets.java b/google-cloud-examples/src/test/java/com/google/cloud/examples/pubsub/snippets/ITTopicSnippets.java new file mode 100644 index 000000000000..57c3c70d04aa --- /dev/null +++ b/google-cloud-examples/src/test/java/com/google/cloud/examples/pubsub/snippets/ITTopicSnippets.java @@ -0,0 +1,118 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.examples.pubsub.snippets; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.Identity; +import com.google.cloud.Page; +import com.google.cloud.Policy; +import com.google.cloud.Role; +import com.google.cloud.pubsub.PubSub; +import com.google.cloud.pubsub.PubSubOptions; +import com.google.cloud.pubsub.SubscriptionId; +import com.google.cloud.pubsub.SubscriptionInfo; +import com.google.cloud.pubsub.Topic; +import com.google.cloud.pubsub.TopicInfo; +import com.google.common.collect.Iterators; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +public class ITTopicSnippets { + + private static final String TOPIC = "it-topic-snippets-topic-" + UUID.randomUUID().toString(); + private static final String SUBSCRIPTION = + "it-topic-snippets-subscription-" + UUID.randomUUID().toString(); + + private static PubSub pubsub; + private static Topic topic; + + @BeforeClass + public static void beforeClass() { + pubsub = PubSubOptions.getDefaultInstance().getService(); + topic = pubsub.create(TopicInfo.of(TOPIC)); + } + + @AfterClass + public static void afterClass() throws Exception { + if (pubsub != null) { + topic.delete(); + pubsub.close(); + } + } + + @Test + public void testTopic() throws ExecutionException, InterruptedException { + TopicSnippets topicSnippets = new TopicSnippets(topic); + Topic updatedTopic = topicSnippets.reload(); + assertEquals(topic, updatedTopic); + updatedTopic = topicSnippets.reloadAsync(); + assertEquals(topic, updatedTopic); + assertNotNull(topicSnippets.publishOneMessage()); + assertNotNull(topicSnippets.publishOneMessageAsync()); + assertEquals(2, topicSnippets.publishMessageList().size()); + assertEquals(2, topicSnippets.publishMessageListAsync().size()); + assertEquals(2, topicSnippets.publishMessages().size()); + assertEquals(2, topicSnippets.publishMessagesAsync().size()); + } + + @Test + public void testTopicSubscriptions() throws ExecutionException, InterruptedException { + TopicSnippets topicSnippets = new TopicSnippets(topic); + pubsub.create(SubscriptionInfo.of(TOPIC, SUBSCRIPTION)); + try { + Page subscriptions = topicSnippets.listSubscriptionsForTopic(); + while (Iterators.size(subscriptions.getValues().iterator()) < 1) { + subscriptions = topicSnippets.listSubscriptionsForTopic(); + } + assertEquals(SUBSCRIPTION, subscriptions.getValues().iterator().next().getSubscription()); + subscriptions = topicSnippets.listSubscriptionsForTopicAsync(); + while (Iterators.size(subscriptions.getValues().iterator()) < 1) { + subscriptions = topicSnippets.listSubscriptionsForTopic(); + } + assertEquals(SUBSCRIPTION, subscriptions.getValues().iterator().next().getSubscription()); + } finally { + pubsub.deleteSubscription(SUBSCRIPTION); + } + } + + @Test + public void testPolicy() throws ExecutionException, InterruptedException { + TopicSnippets topicSnippets = new TopicSnippets(topic); + Policy policy = topicSnippets.getPolicy(); + assertNotNull(policy); + assertEquals(policy, topicSnippets.getPolicyAsync()); + policy = topicSnippets.replacePolicy(); + assertTrue(policy.getBindings().get(Role.viewer()).contains(Identity.allAuthenticatedUsers())); + policy = topic.replacePolicy(policy.toBuilder() + .removeIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build()); + assertFalse(policy.getBindings().containsKey(Role.viewer())); + policy = topicSnippets.replacePolicyAsync(); + assertTrue(policy.getBindings().get(Role.viewer()).contains(Identity.allAuthenticatedUsers())); + assertTrue(topicSnippets.delete()); + assertFalse(topicSnippets.deleteAsync()); + } +} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/AckDeadlineRenewer.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/AckDeadlineRenewer.java new file mode 100644 index 000000000000..8bc81606c1ab --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/AckDeadlineRenewer.java @@ -0,0 +1,317 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import com.google.cloud.Clock; +import com.google.cloud.GrpcServiceOptions.ExecutorFactory; +import com.google.common.base.MoreObjects; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Multimaps; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * Class for an automatic ack deadline renewer. An ack deadline renewer automatically renews the + * acknowledge deadline of messages added to it (via {@link #add(String, String)} or + * {@link #add(String, Iterable)}. The acknowledge deadlines of added messages are renewed until the + * messages are explicitly removed using {@link #remove(String, String)}. + */ +class AckDeadlineRenewer implements AutoCloseable { + + private static final int MIN_DEADLINE_MILLIS = 10_000; + private static final int DEADLINE_SLACK_MILLIS = 1_000; + private static final int RENEW_THRESHOLD_MILLIS = 3_000; + private static final int NEXT_RENEWAL_THRESHOLD_MILLIS = 1_000; + + private final PubSub pubsub; + private final ScheduledExecutorService executor; + private final ExecutorFactory executorFactory; + private final Clock clock; + private final Queue messageQueue; + private final Map messageDeadlines; + private final Object lock = new Object(); + private final Object futureLock = new Object(); + private Future renewerFuture; + private boolean closed; + + /** + * This class holds the identity of a message to renew: subscription and acknowledge id. + */ + private static class MessageId { + + private final String subscription; + private final String ackId; + + MessageId(String subscription, String ackId) { + this.subscription = subscription; + this.ackId = ackId; + } + + String subscription() { + return subscription; + } + + String ackId() { + return ackId; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof MessageId)) { + return false; + } + MessageId other = (MessageId) obj; + return Objects.equals(other.subscription, this.subscription) + && Objects.equals(other.ackId, this.ackId); + } + + @Override + public int hashCode() { + return Objects.hash(subscription, ackId); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("subscription", subscription) + .add("ackId", ackId) + .toString(); + } + } + + /** + * This class holds the identity of a message to renew and its expected ack deadline. + */ + private static final class Message { + + private final MessageId messageId; + private final Long deadline; + + Message(MessageId messageId, Long deadline) { + this.messageId = messageId; + this.deadline = deadline; + } + + MessageId messageId() { + return messageId; + } + + Long expectedDeadline() { + return deadline; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Message)) { + return false; + } + Message other = (Message) obj; + return Objects.equals(other.messageId, this.messageId) + && Objects.equals(other.deadline, this.deadline); + } + + @Override + public int hashCode() { + return Objects.hash(messageId, deadline); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("messageId", messageId) + .add("expectedDeadline", deadline) + .toString(); + } + } + + AckDeadlineRenewer(PubSub pubsub) { + PubSubOptions options = pubsub.getOptions(); + this.pubsub = pubsub; + this.executorFactory = options.getExecutorFactory(); + this.executor = executorFactory.get(); + this.clock = options.getClock(); + this.messageQueue = new LinkedList<>(); + this.messageDeadlines = new HashMap<>(); + } + + private void unsetAndScheduleNextRenewal() { + synchronized (futureLock) { + renewerFuture = null; + scheduleNextRenewal(); + } + } + + private void scheduleNextRenewal() { + // Schedules next renewal if there are still messages to process and no renewals scheduled that + // could handle them, otherwise does nothing + Message nextMessage; + synchronized (lock) { + Message peek = messageQueue.peek(); + // We remove from the queue messages that were removed from the ack deadline renewer (and + // possibly re-added) + while (peek != null && (!messageDeadlines.containsKey(peek.messageId()) + || messageDeadlines.get(peek.messageId()) > peek.expectedDeadline())) { + messageQueue.poll(); + peek = messageQueue.peek(); + } + nextMessage = peek; + } + synchronized (futureLock) { + if (renewerFuture == null && nextMessage != null) { + long delay = + (nextMessage.expectedDeadline() - clock.millis()) - NEXT_RENEWAL_THRESHOLD_MILLIS; + renewerFuture = executor.schedule(new Runnable() { + @Override + public void run() { + renewAckDeadlines(); + } + }, delay, TimeUnit.MILLISECONDS); + } + } + } + + private void renewAckDeadlines() { + ListMultimap messagesToRenewNext = LinkedListMultimap.create(); + // At every activation we renew all ack deadlines that will expire in the following + // RENEW_THRESHOLD_MILLIS + long threshold = clock.millis() + RENEW_THRESHOLD_MILLIS; + Message message; + while ((message = nextMessageToRenew(threshold)) != null) { + // If the expected deadline is null the message was removed and we must ignore it, otherwise + // we renew its ack deadline + if (message.expectedDeadline() != null) { + messagesToRenewNext.put(message.messageId().subscription(), message.messageId().ackId()); + } + } + for (Map.Entry> entry : Multimaps.asMap(messagesToRenewNext).entrySet()) { + // We send all ack deadline renewals for a subscription + pubsub.modifyAckDeadlineAsync(entry.getKey(), MIN_DEADLINE_MILLIS, TimeUnit.MILLISECONDS, + entry.getValue()); + } + unsetAndScheduleNextRenewal(); + } + + private Message nextMessageToRenew(long threshold) { + synchronized (lock) { + Message message = messageQueue.peek(); + // if the queue is empty or the next expected deadline is after threshold we stop + if (message == null || message.expectedDeadline() > threshold) { + return null; + } + MessageId messageId = messageQueue.poll().messageId(); + // Check if the next expected deadline changed. This can happen if the message was removed + // from the ack deadline renewer or if it was nacked and then pulled again + Long deadline = messageDeadlines.get(messageId); + if (deadline == null || deadline > threshold) { + // the message was removed (deadline == null) or removed and then added back + // (deadline > threshold), we should not renew its deadline (yet) + return new Message(messageId, null); + } else { + // Message deadline must be renewed, we must submit it again to the renewer + add(messageId.subscription(), messageId.ackId()); + return new Message(messageId, deadline); + } + } + } + + /** + * Adds a new message for which the acknowledge deadline should be automatically renewed. The + * message is identified by the subscription from which it was pulled and its acknowledge id. + * Auto-renewal will take place until the message is removed (see + * {@link #remove(String, String)}). + * + * @param subscription the subscription from which the message has been pulled + * @param ackId the message's acknowledge id + */ + void add(String subscription, String ackId) { + synchronized (lock) { + long deadline = clock.millis() + MIN_DEADLINE_MILLIS - DEADLINE_SLACK_MILLIS; + Message message = new Message(new MessageId(subscription, ackId), deadline); + messageQueue.add(message); + messageDeadlines.put(message.messageId(), deadline); + } + scheduleNextRenewal(); + } + + /** + * Adds new messages for which the acknowledge deadlined should be automatically renewed. The + * messages are identified by the subscription from which they were pulled and their + * acknowledge id. Auto-renewal will take place until the messages are removed (see + * {@link #remove(String, String)}). + * + * @param subscription the subscription from which the messages have been pulled + * @param ackIds the acknowledge ids of the messages + */ + void add(String subscription, Iterable ackIds) { + synchronized (lock) { + long deadline = clock.millis() + MIN_DEADLINE_MILLIS - DEADLINE_SLACK_MILLIS; + for (String ackId : ackIds) { + Message message = new Message(new MessageId(subscription, ackId), deadline); + messageQueue.add(message); + messageDeadlines.put(message.messageId(), deadline); + } + } + scheduleNextRenewal(); + } + + /** + * Removes a message from this {@code AckDeadlineRenewer}. The message is identified by the + * subscription from which it was pulled and its acknowledge id. Once the message is removed from + * this {@code AckDeadlineRenewer}, automated ack deadline renewals will stop. + * + * @param subscription the subscription from which the message has been pulled + * @param ackId the message's acknowledge id + */ + void remove(String subscription, String ackId) { + synchronized (lock) { + messageDeadlines.remove(new MessageId(subscription, ackId)); + } + } + + @Override + public void close() throws Exception { + if (closed) { + return; + } + closed = true; + synchronized (lock) { + messageDeadlines.clear(); + messageQueue.clear(); + } + synchronized (futureLock) { + if (renewerFuture != null) { + renewerFuture.cancel(true); + } + } + executorFactory.release(executor); + } +} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Message.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Message.java new file mode 100644 index 000000000000..d728dad43aea --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Message.java @@ -0,0 +1,454 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.cloud.ByteArray; +import com.google.common.base.Function; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableMap; +import com.google.protobuf.ByteString; +import com.google.protobuf.Timestamp; +import com.google.pubsub.v1.PubsubMessage; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * A Google Cloud Pub/Sub message. A message is the combination of data and (optional) attributes + * that a publisher sends to a topic and is eventually delivered to subscribers. + * + *

    Message attributes are key-value pairs that a publisher can define for a message. For example, + * a key {@code iana.org/language_tag} and value {@code en} could be added to messages to mark them + * as readable by an English-speaking subscriber. + * + *

    To be published, a message must have a non-empty payload, or at least one attribute. + * + * @see Pub/Sub Data Model + */ +public class Message implements Serializable { + + static final Function TO_PB_FUNCTION = + new Function() { + @Override + public PubsubMessage apply(Message message) { + return message.toPb(); + } + }; + + private static final long serialVersionUID = -1436515787233340634L; + private static final long NANOS_PER_MILLISECOND = 1000000; + private static final long MILLIS_PER_SECOND = 1000; + + private final String id; + private final InternalByteArray payload; + private final ImmutableMap attributes; + private final Long publishTime; + + private static final class InternalByteArray extends ByteArray { + + private static final long serialVersionUID = -3330181485911805428L; + + InternalByteArray(ByteString byteString) { + super(byteString); + } + + InternalByteArray(ByteArray byteArray) { + super(byteArray); + } + + @Override + protected ByteString getByteString() { + return super.getByteString(); + } + } + + /** + * Builder for {@code Message} objects. + */ + public abstract static class Builder { + + abstract Builder setId(String id); + + /** + * Sets the message payload to the provided string. The string is enconded {@code UTF-8}. + */ + @Deprecated + public abstract Builder payload(String payload); + + /** + * Sets the message payload to the provided string. The string is enconded {@code UTF-8}. + */ + public abstract Builder setPayload(String payload); + + /** + * Sets the message payload to the provided {@link ByteArray}. + */ + @Deprecated + public abstract Builder payload(ByteArray payload); + + /** + * Sets the message payload to the provided {@link ByteArray}. + */ + public abstract Builder setPayload(ByteArray payload); + + /** + * Sets the message attributes to the provided map. Message attributes are key-value pairs that + * a publisher can define for a message. For example, a key {@code iana.org/language_tag} and + * value {@code en} could be added to messages to mark them as readable by an English-speaking + * subscriber. + */ + @Deprecated + public abstract Builder attributes(Map attributes); + + /** + * Sets the message attributes to the provided map. Message attributes are key-value pairs that + * a publisher can define for a message. For example, a key {@code iana.org/language_tag} and + * value {@code en} could be added to messages to mark them as readable by an English-speaking + * subscriber. + */ + public abstract Builder setAttributes(Map attributes); + + /** + * Adds a new attribute to the message attributes. If an attribute with name {@code name} was + * already set, its value is updated. + */ + public abstract Builder addAttribute(String name, String value); + + /** + * Removes an attribute give its name from the message attributes. + */ + public abstract Builder removeAttribute(String name); + + /** + * Clears all message attributes. + */ + public abstract Builder clearAttributes(); + + abstract Builder setPublishTime(long publishTime); + + /** + * Creates a message object. + */ + public abstract Message build(); + } + + static final class BuilderImpl extends Builder { + + private String id; + private ByteArray payload; + private Map attributes = new HashMap<>(); + private Long publishTime; + + private BuilderImpl() {} + + BuilderImpl(Message message) { + id = message.id; + payload = message.payload; + attributes = new HashMap<>(message.attributes); + publishTime = message.publishTime; + } + + @Override + BuilderImpl setId(String id) { + this.id = checkNotNull(id); + return this; + } + + @Override + @Deprecated + public Builder payload(String payload) { + return setPayload(payload); + } + + @Override + public Builder setPayload(String payload) { + return setPayload(ByteArray.copyFrom(payload)); + } + + @Override + @Deprecated + public Builder payload(ByteArray payload) { + return setPayload(payload); + } + + @Override + public Builder setPayload(ByteArray payload) { + this.payload = payload; + return this; + } + + @Override + public Builder addAttribute(String name, String value) { + attributes.put(name, value); + return this; + } + + @Override + @Deprecated + public Builder attributes(Map attributes) { + return setAttributes(attributes); + } + + @Override + public Builder setAttributes(Map attributes) { + this.attributes = new HashMap<>(attributes); + return this; + } + + @Override + public Builder removeAttribute(String name) { + attributes.remove(name); + return this; + } + + @Override + public Builder clearAttributes() { + attributes.clear(); + return this; + } + + @Override + Builder setPublishTime(long publishTime) { + this.publishTime = publishTime; + return this; + } + + @Override + public Message build() { + return new Message(this); + } + } + + Message(BuilderImpl builder) { + id = builder.id; + payload = new InternalByteArray(checkNotNull(builder.payload)); + attributes = ImmutableMap.copyOf(builder.attributes); + publishTime = builder.publishTime; + } + + /** + * Returns the time in milliseconds at which the message was published. This value is set by the + * server when it receives the publish call. If not set, this method returns {@code null}. + */ + @Deprecated + public Long publishTime() { + return getPublishTime(); + } + + /** + * Returns the time in milliseconds at which the message was published. This value is set by the + * server when it receives the publish call. If not set, this method returns {@code null}. + */ + public Long getPublishTime() { + return publishTime; + } + + /** + * Returns the message attributes. Message attributes are key-value pairs that a publisher can + * define for a message. For example, a key {@code iana.org/language_tag} and value {@code en} + * could be added to messages to mark them as readable by an English-speaking subscriber. + */ + @Deprecated + public Map attributes() { + return getAttributes(); + } + + /** + * Returns the message attributes. Message attributes are key-value pairs that a publisher can + * define for a message. For example, a key {@code iana.org/language_tag} and value {@code en} + * could be added to messages to mark them as readable by an English-speaking subscriber. + */ + public Map getAttributes() { + return attributes; + } + + /** + * Returns the id of this message, set by the server when the message is published. The id is + * guaranteed to be unique within the topic. This value may be read by a subscriber that receives + * a Pub/Sub message via a pull call or a push delivery. If not set, this method returns + * {@code null}. + */ + @Deprecated + public String id() { + return getId(); + } + + /** + * Returns the id of this message, set by the server when the message is published. The id is + * guaranteed to be unique within the topic. This value may be read by a subscriber that receives + * a Pub/Sub message via a pull call or a push delivery. If not set, this method returns + * {@code null}. + */ + public String getId() { + return id; + } + + /** + * Returns the message payload as a string, decoded using {@code UTF-8}. + */ + @Deprecated + public String payloadAsString() { + return getPayloadAsString(); + } + + /** + * Returns the message payload as a string, decoded using {@code UTF-8}. + */ + public String getPayloadAsString() { + return payload.toStringUtf8(); + } + + /** + * Returns the message payload. + */ + @Deprecated + public ByteArray payload() { + return getPayload(); + } + + /** + * Returns the message payload. + */ + public ByteArray getPayload() { + return payload; + } + + final boolean baseEquals(Message message) { + return Objects.equals(id, message.id) + && Objects.equals(payload, message.payload) + && Objects.equals(attributes, message.attributes) + && Objects.equals(publishTime, message.publishTime); + } + + @Override + public boolean equals(Object obj) { + return obj == this + || obj != null + && obj.getClass().equals(Message.class) + && baseEquals((Message) obj); + } + + @Override + public int hashCode() { + return Objects.hash(id, payload, attributes, publishTime); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("id", id) + .add("payload", payload) + .add("attributes", attributes) + .add("publishTime", publishTime) + .toString(); + } + + PubsubMessage toPb() { + PubsubMessage.Builder builder = PubsubMessage.newBuilder(); + if (id != null) { + builder.setMessageId(id); + } + builder.setData(payload.getByteString()); + builder.putAllAttributes(attributes); + Timestamp.Builder tsBuilder = Timestamp.newBuilder(); + if (publishTime != null) { + tsBuilder.setSeconds(publishTime / MILLIS_PER_SECOND); + tsBuilder.setNanos((int) (publishTime % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND)); + } + builder.setPublishTime(tsBuilder); + return builder.build(); + } + + static Message fromPb(PubsubMessage messagePb) { + Builder builder = newBuilder(new InternalByteArray(messagePb.getData())); + if (messagePb.hasPublishTime()) { + Timestamp ts = messagePb.getPublishTime(); + Long millis = ts.getSeconds() * MILLIS_PER_SECOND + ts.getNanos() / NANOS_PER_MILLISECOND; + if (millis != 0) { + builder.setPublishTime(millis); + } + } + if (!Objects.equals(messagePb.getMessageId(), "")) { + builder.setId(messagePb.getMessageId()); + } + for (Map.Entry entry : messagePb.getAttributesMap().entrySet()) { + builder.addAttribute(entry.getKey(), entry.getValue()); + } + return builder.build(); + } + + /** + * Returns a builder for the message object. + */ + public Builder toBuilder() { + return new BuilderImpl(this); + } + + /** + * Creates a {@code Message} object given the payload as a string. The string is enconded using + * {@code UTF-8}. + */ + public static Message of(String payload) { + return newBuilder(payload).build(); + } + + /** + * Creates a {@code Message} object given the payload as a {@link ByteArray}. To be published a + * message must have a non-empty payload. + */ + public static Message of(ByteArray payload) { + return newBuilder(payload).build(); + } + + /** + * Creates a builder for {@code Message} objects given the payload as a string. The string is + * enconded using {@code UTF-8}. To be published a message must have a non-empty payload. + */ + @Deprecated + public static Builder builder(String payload) { + return newBuilder(payload); + } + + /** + * Creates a builder for {@code Message} objects given the payload as a string. The string is + * enconded using {@code UTF-8}. To be published a message must have a non-empty payload. + */ + public static Builder newBuilder(String payload) { + return new BuilderImpl().setPayload(payload); + } + + /** + * Creates a builder for {@code Message} objects given the payload as a {@link ByteArray}. To be + * published a message must have a non-empty payload, or at least one attribute. + */ + @Deprecated + public static Builder builder(ByteArray payload) { + return newBuilder(payload); + } + + /** + * Creates a builder for {@code Message} objects given the payload as a {@link ByteArray}. To be + * published a message must have a non-empty payload, or at least one attribute. + */ + public static Builder newBuilder(ByteArray payload) { + return new BuilderImpl().setPayload(payload); + } +} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Option.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Option.java new file mode 100644 index 000000000000..b64dc80733d4 --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Option.java @@ -0,0 +1,77 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.MoreObjects; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Base class for Pub/Sub operation options. + */ +abstract class Option implements Serializable { + + private static final long serialVersionUID = 4956295408130172192L; + + private final OptionType optionType; + private final Object value; + + interface OptionType { + + String name(); + } + + Option(OptionType optionType, Object value) { + this.optionType = checkNotNull(optionType); + this.value = value; + } + + @SuppressWarnings("unchecked") + T getOptionType() { + return (T) optionType; + } + + Object getValue() { + return value; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Option)) { + return false; + } + Option other = (Option) obj; + return Objects.equals(optionType, other.optionType) + && Objects.equals(value, other.value); + } + + @Override + public int hashCode() { + return Objects.hash(optionType, value); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", optionType.name()) + .add("value", value) + .toString(); + } +} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PolicyMarshaller.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PolicyMarshaller.java new file mode 100644 index 000000000000..5e910db00dd6 --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PolicyMarshaller.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import com.google.cloud.Policy; + +final class PolicyMarshaller extends Policy.DefaultMarshaller { + + static final PolicyMarshaller INSTANCE = new PolicyMarshaller(); + + private PolicyMarshaller() {} + + @Override + protected com.google.iam.v1.Policy toPb(Policy policy) { + return super.toPb(policy); + } + + @Override + protected Policy fromPb(com.google.iam.v1.Policy policyPb) { + return super.fromPb(policyPb); + } +} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java new file mode 100644 index 000000000000..3e45fce3eb11 --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java @@ -0,0 +1,1446 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import com.google.cloud.AsyncPage; +import com.google.cloud.GrpcServiceOptions.ExecutorFactory; +import com.google.cloud.Page; +import com.google.cloud.Policy; +import com.google.cloud.Service; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * An interface for Google Cloud Pub/Sub. + * + * @see Google Cloud Pub/Sub + */ +public interface PubSub extends AutoCloseable, Service { + + /** + * Class for specifying options for listing topics and subscriptions. + */ + final class ListOption extends Option { + + private static final long serialVersionUID = 6517442127283383124L; + + enum OptionType implements Option.OptionType { + PAGE_SIZE, PAGE_TOKEN; + + @SuppressWarnings("unchecked") + T get(Map options) { + return (T) options.get(this); + } + + String getString(Map options) { + return get(options); + } + + Integer getInteger(Map options) { + return get(options); + } + } + + private ListOption(OptionType option, Object value) { + super(option, value); + } + + /** + * Returns an option to specify the maximum number of resources returned per page. + */ + public static ListOption pageSize(int pageSize) { + return new ListOption(OptionType.PAGE_SIZE, pageSize); + } + + /** + * Returns an option to specify the page token from which to start listing resources. + */ + public static ListOption pageToken(String pageToken) { + return new ListOption(OptionType.PAGE_TOKEN, pageToken); + } + } + + /** + * Class for specifying options for pulling messages. + */ + final class PullOption extends Option { + + private static final long serialVersionUID = 4792164134340316582L; + + enum OptionType implements Option.OptionType { + EXECUTOR_FACTORY, + MAX_QUEUED_CALLBACKS; + + @SuppressWarnings("unchecked") + T get(Map options) { + return (T) options.get(this); + } + + Integer getInteger(Map options) { + return get(options); + } + + ExecutorFactory getExecutorFactory(Map options) { + return get(options); + } + } + + private PullOption(Option.OptionType option, Object value) { + super(option, value); + } + + /** + * Returns an option to specify the maximum number of messages that can be queued in the message + * consumer at any time. Queued messages are already pulled messages that are either waiting to + * be processed or being processed. Queued messages will have their acknowledge deadline renewed + * until they are acknowledged or "nacked". If not provided, at most 100 messages can be in the + * queue. + */ + public static PullOption maxQueuedCallbacks(int maxQueuedCallbacks) { + return new PullOption(OptionType.MAX_QUEUED_CALLBACKS, maxQueuedCallbacks); + } + + /** + * Returns an option to specify the executor used to execute message processor callbacks. The + * executor determines the number of messages that can be processed at the same time. If not + * provided, a single-threaded executor is used to execute message processor callbacks. + * + *

    The {@link ExecutorFactory} object can be used to handle creation and release of the + * executor, possibly reusing existing executors. {@link ExecutorFactory#get()} is called when + * the message consumer is created. {@link ExecutorFactory#release(ExecutorService)} is called + * when the message consumer is closed. + * + *

    For the created option to be serializable, the provided executor factory should implement + * {@link java.io.Serializable}. + * + * @param executorFactory the executor factory. + */ + public static PullOption executorFactory(ExecutorFactory executorFactory) { + return new PullOption(OptionType.EXECUTOR_FACTORY, executorFactory); + } + } + + /** + * A callback to process pulled messages. The received message will be ack'ed upon successful + * return or nack'ed if exception is thrown. + */ + interface MessageProcessor { + /** + * Processes the received {@code message}. If this method returns correctly the message is + * ack'ed. If this method throws an exception the message is nack'ed. + */ + void process(Message message) throws Exception; + } + + /** + * An interface to control a message consumer. + */ + interface MessageConsumer extends AutoCloseable { + + /** + * Stops pulling messages from the subscription associated with this {@code MessageConsumer} and + * frees all resources. Messages that have already been pulled are processed before closing. + */ + @Override + void close() throws Exception; + } + + /** + * Creates a new topic. + * + *

    Example of creating a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * TopicInfo topicInfo = TopicInfo.of(topicName);
    +   * Topic topic = pubsub.create(topicInfo);
    +   * }
    + * + * @return the created topic + * @throws PubSubException upon failure + */ + Topic create(TopicInfo topic); + + /** + * Sends a request for creating a topic. This method returns a {@code Future} object to consume + * the result. {@link Future#get()} returns the created topic. + * + *

    Example of asynchronously creating a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * TopicInfo topicInfo = TopicInfo.of(topicName);
    +   * Future future = pubsub.createAsync(topicInfo);
    +   * // ...
    +   * Topic topic = future.get();
    +   * }
    + * + */ + Future createAsync(TopicInfo topic); + + /** + * Returns the requested topic or {@code null} if not found. + * + *

    Example of getting a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * Topic topic = pubsub.getTopic(topicName);
    +   * if (topic == null) {
    +   *   // topic was not found
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + Topic getTopic(String topic); + + /** + * Sends a request for getting a topic. This method returns a {@code Future} object to consume the + * result. {@link Future#get()} returns the requested topic or {@code null} if not found. + * + *

    Example of asynchronously getting a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * Future future = pubsub.getTopicAsync(topicName);
    +   * // ...
    +   * Topic topic = future.get();
    +   * if (topic == null) {
    +   *   // topic was not found
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + Future getTopicAsync(String topic); + + /** + * Deletes the requested topic. + * + *

    Example of deleting a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * boolean deleted = pubsub.deleteTopic(topicName);
    +   * if (deleted) {
    +   *   // the topic was deleted
    +   * } else {
    +   *   // the topic was not found
    +   * }
    +   * }
    + * + * @return {@code true} if the topic was deleted, {@code false} if it was not found + */ + boolean deleteTopic(String topic); + + /** + * Sends a request for deleting a topic. This method returns a {@code Future} object to consume + * the result. {@link Future#get()} returns {@code true} if the topic was deleted, {@code false} + * if it was not found. + * + *

    Example of asynchronously deleting a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * Future future = pubsub.deleteTopicAsync(topicName);
    +   * // ...
    +   * boolean deleted = future.get();
    +   * if (deleted) {
    +   *   // the topic was deleted
    +   * } else {
    +   *   // the topic was not found
    +   * }
    +   * }
    + * + */ + Future deleteTopicAsync(String topic); + + /** + * Lists the topics. This method returns a {@link Page} object that can be used to consume + * paginated results. Use {@link ListOption} to specify the page size or the page token from which + * to start listing topics. + * + *

    Example of listing topics, specifying the page size. + *

     {@code
    +   * Page topics = pubsub.listTopics(ListOption.pageSize(100));
    +   * Iterator topicIterator = topics.iterateAll();
    +   * while (topicIterator.hasNext()) {
    +   *   Topic topic = topicIterator.next();
    +   *   // do something with the topic
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + Page listTopics(ListOption... options); + + /** + * Sends a request for listing topics. This method returns a {@code Future} object to consume + * the result. {@link Future#get()} returns an {@link AsyncPage} object that can be used to + * asynchronously handle paginated results. Use {@link ListOption} to specify the page size or the + * page token from which to start listing topics. + * + *

    Example of asynchronously listing topics, specifying the page size. + *

     {@code
    +   * Future> future = pubsub.listTopicsAsync(ListOption.pageSize(100));
    +   * // ...
    +   * AsyncPage topics = future.get();
    +   * Iterator topicIterator = topics.iterateAll();
    +   * while (topicIterator.hasNext()) {
    +   *   Topic topic = topicIterator.next();
    +   *   // do something with the topic
    +   * }
    +   * }
    + * + */ + Future> listTopicsAsync(ListOption... options); + + /** + * Publishes a message to the provided topic. This method returns a service-generated id for the + * published message. Service-generated ids are guaranteed to be unique within the topic. + * + *

    Example of publishing one message to a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * Message message = Message.of("payload");
    +   * String messageId = pubsub.publish(topicName, message);
    +   * }
    + * + * @param topic the topic where the message is published + * @param message the message to publish + * @return a unique service-generated id for the message + * @throws PubSubException upon failure, if the topic does not exist or if the message has empty + * payload and no attributes + */ + String publish(String topic, Message message); + + /** + * Sends a request for publishing a message to the provided topic. This method returns a + * {@code Future} object to consume the result. {@link Future#get()} returns a service-generated + * id for the published message. Service-generated ids are guaranteed to be unique within the + * topic. + * + *

    Example of asynchronously publishing one message to a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * Message message = Message.of("payload");
    +   * Future future = pubsub.publishAsync(topicName, message);
    +   * // ...
    +   * String messageId = future.get();
    +   * }
    + * + * @param topic the topic where the message is published + * @param message the message to publish + * @return a {@code Future} for the unique service-generated id for the message + */ + Future publishAsync(String topic, Message message); + + /** + * Publishes a number of messages to the provided topic. This method returns a list of + * service-generated ids for the published messages. Service-generated ids are guaranteed to be + * unique within the topic. + * + *

    Example of publishing some messages to a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * Message message1 = Message.of("payload1");
    +   * Message message2 = Message.of("payload2");
    +   * List messageIds = pubsub.publish(topicName, message1, message2);
    +   * }
    + * + * @param topic the topic where the message is published + * @param message the first message to publish + * @param messages other messages to publish + * @return a list of unique, service-generated, ids. Ids are in the same order as the messages. + * @throws PubSubException upon failure, if the topic does not exist or if one of the messages has + * empty payload and no attributes + */ + List publish(String topic, Message message, Message... messages); + + /** + * Sends a request to publish a number of messages to the provided topic. This method returns a + * {@code Future} object to consume the result. {@link Future#get()} returns a list of + * service-generated ids for the published messages. Service-generated ids are guaranteed to be + * unique within the topic. + * + *

    Example of asynchronously publishing some messages to a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * Message message1 = Message.of("payload1");
    +   * Message message2 = Message.of("payload2");
    +   * Future> future = pubsub.publishAsync(topicName, message1, message2);
    +   * // ...
    +   * List messageIds = future.get();
    +   * }
    + * + * @param topic the topic where the message is published + * @param message the first message to publish + * @param messages other messages to publish + * @return a {@code Future} for the unique, service-generated ids. Ids are in the same order as + * the messages. + */ + Future> publishAsync(String topic, Message message, Message... messages); + + /** + * Publishes a number of messages to the provided topic. This method returns a list of + * service-generated ids for the published messages. Service-generated ids are guaranteed to be + * unique within the topic. + * + *

    Example of publishing a list of messages to a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * List messages = new LinkedList<>();
    +   * messages.add(Message.of("payload1"));
    +   * messages.add(Message.of("payload2"));
    +   * List messageIds = pubsub.publish(topicName, messages);
    +   * }
    + * + * @param topic the topic where the message is published + * @param messages the messages to publish + * @return a list of unique, service-generated, ids. Ids are in the same order as the messages. + * @throws PubSubException upon failure, if the topic does not exist or if one of the messages has + * empty payload and no attributes + */ + List publish(String topic, Iterable messages); + + /** + * Sends a request to publish a number of messages to the provided topic. This method returns a + * {@code Future} object to consume the result. {@link Future#get()} returns a list of + * service-generated ids for the published messages. Service-generated ids are guaranteed to be + * unique within the topic. + * + *

    Example of asynchronously publishing a list of messages to a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * List messages = new LinkedList<>();
    +   * messages.add(Message.of("payload1"));
    +   * messages.add(Message.of("payload2"));
    +   * Future> future = pubsub.publishAsync(topicName, messages);
    +   * // ...
    +   * List messageIds = future.get();
    +   * }
    + * + * @param topic the topic where the message is published + * @param messages the messages to publish + * @return a {@code Future} for the unique, service-generated ids. Ids are in the same order as + * the messages + */ + Future> publishAsync(String topic, Iterable messages); + + /** + * Creates a new subscription. + * + *

    Example of creating a pull subscription for a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * String subscriptionName = "my_subscription_name";
    +   * SubscriptionInfo subscriptionInfo = SubscriptionInfo.of(topicName, subscriptionName);
    +   * Subscription subscription = pubsub.create(subscriptionInfo);
    +   * }
    + * + * @return the created subscription + * @throws PubSubException upon failure + */ + Subscription create(SubscriptionInfo subscription); + + /** + * Sends a request for creating a subscription. This method returns a {@code Future} object to + * consume the result. {@link Future#get()} returns the created subscription. + * + *

    Example of asynchronously creating a pull subscription for a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * String subscriptionName = "my_subscription_name";
    +   * SubscriptionInfo subscriptionInfo = SubscriptionInfo.of(topicName, subscriptionName);
    +   * Future future = pubsub.createAsync(subscriptionInfo);
    +   * // ...
    +   * Subscription subscription = future.get();
    +   * }
    + * + */ + Future createAsync(SubscriptionInfo subscription); + + /** + * Returns the requested subscription or {@code null} if not found. + * + *

    Example of getting a subscription. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * Subscription subscription = pubsub.getSubscription(subscriptionName);
    +   * if (subscription == null) {
    +   *   // subscription was not found
    +   * }
    +   * }
    + * + */ + Subscription getSubscription(String subscription); + + /** + * Sends a request for getting a subscription. This method returns a {@code Future} object to + * consume the result. {@link Future#get()} returns the requested subscription or {@code null} if + * not found. + * + *

    Example of asynchronously getting a subscription. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * Future future = pubsub.getSubscriptionAsync(subscriptionName);
    +   * // ...
    +   * Subscription subscription = future.get();
    +   * if (subscription == null) {
    +   *   // subscription was not found
    +   * }
    +   * }
    + * + */ + Future getSubscriptionAsync(String subscription); + + /** + * Sets the push configuration for a specified subscription. This may be used to change a push + * subscription to a pull one (passing a {@code null} {@code pushConfig} parameter) or vice versa. + * This methods can also be used to change the endpoint URL and other attributes of a push + * subscription. Messages will accumulate for delivery regardless of changes to the push + * configuration. + * + *

    Example of replacing the push configuration of a subscription, setting the push endpoint. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String endpoint = "https://www.example.com/push";
    +   * PushConfig pushConfig = PushConfig.of(endpoint);
    +   * pubsub.replacePushConfig(subscriptionName, pushConfig);
    +   * }
    + * + *

    Example of replacing the push configuration of a subscription, making it a pull + * subscription. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * pubsub.replacePushConfig(subscriptionName, null);
    +   * }
    + * + * @param subscription the subscription for which to replace push configuration + * @param pushConfig the new push configuration. Use {@code null} to unset it + * @throws PubSubException upon failure, or if the subscription does not exist + */ + void replacePushConfig(String subscription, PushConfig pushConfig); + + /** + * Sends a request for updating the push configuration for a specified subscription. This may be + * used to change a push subscription to a pull one (passing a {@code null} {@code pushConfig} + * parameter) or vice versa. This methods can also be used to change the endpoint URL and other + * attributes of a push subscription. Messages will accumulate for delivery regardless of changes + * to the push configuration. The method returns a {@code Future} object that can be used to wait + * for the replace operation to be completed. + * + *

    Example of asynchronously replacing the push configuration of a subscription, setting the + * push endpoint. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String endpoint = "https://www.example.com/push";
    +   * PushConfig pushConfig = PushConfig.of(endpoint);
    +   * Future future = pubsub.replacePushConfigAsync(subscriptionName, pushConfig);
    +   * // ...
    +   * future.get();
    +   * }
    + * + *

    Example of asynchronously replacing the push configuration of a subscription, making it a + * pull subscription. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * Future future = pubsub.replacePushConfigAsync(subscriptionName, null);
    +   * // ...
    +   * future.get();
    +   * }
    + * + * @param subscription the subscription for which to replace push configuration + * @param pushConfig the new push configuration. Use {@code null} to unset it + * @return a {@code Future} to wait for the replace operation to be completed. + */ + Future replacePushConfigAsync(String subscription, PushConfig pushConfig); + + /** + * Deletes the requested subscription. + * + *

    Example of deleting a subscription. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * boolean deleted = pubsub.deleteSubscription(subscriptionName);
    +   * if (deleted) {
    +   *   // the subscription was deleted
    +   * } else {
    +   *   // the subscription was not found
    +   * }
    +   * }
    + * + * @return {@code true} if the subscription was deleted, {@code false} if it was not found + * @throws PubSubException upon failure + */ + boolean deleteSubscription(String subscription); + + /** + * Sends a request for deleting a subscription. This method returns a {@code Future} object to + * consume the result. {@link Future#get()} returns {@code true} if the subscription was deleted, + * {@code false} if it was not found. + * + *

    Example of asynchronously deleting a subscription. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * Future future = pubsub.deleteSubscriptionAsync(subscriptionName);
    +   * // ...
    +   * boolean deleted = future.get();
    +   * if (deleted) {
    +   *   // the subscription was deleted
    +   * } else {
    +   *   // the subscription was not found
    +   * }
    +   * }
    + * + */ + Future deleteSubscriptionAsync(String subscription); + + /** + * Lists the subscriptions. This method returns a {@link Page} object that can be used to consume + * paginated results. Use {@link ListOption} to specify the page size or the page token from which + * to start listing subscriptions. + * + *

    Example of listing subscriptions, specifying the page size. + *

     {@code
    +   * Page subscriptions = pubsub.listSubscriptions(ListOption.pageSize(100));
    +   * Iterator subscriptionIterator = subscriptions.iterateAll();
    +   * while (subscriptionIterator.hasNext()) {
    +   *   Subscription subscription = subscriptionIterator.next();
    +   *   // do something with the subscription
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + Page listSubscriptions(ListOption... options); + + /** + * Sends a request for listing subscriptions. This method returns a {@code Future} object to + * consume the result. {@link Future#get()} returns an {@link AsyncPage} object that can be used + * to asynchronously handle paginated results. Use {@link ListOption} to specify the page size or + * the page token from which to start listing subscriptions. + * + *

    Example of asynchronously listing subscriptions, specifying the page size. + *

     {@code
    +   * Future> future =
    +   *     pubsub.listSubscriptionsAsync(ListOption.pageSize(100));
    +   * // ...
    +   * AsyncPage subscriptions = future.get();
    +   * Iterator subscriptionIterator = subscriptions.iterateAll();
    +   * while (subscriptionIterator.hasNext()) {
    +   *   Subscription subscription = subscriptionIterator.next();
    +   *   // do something with the subscription
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + Future> listSubscriptionsAsync(ListOption... options); + + /** + * Lists the identities of the subscriptions for the provided topic. This method returns a + * {@link Page} object that can be used to consume paginated results. Use {@link ListOption} to + * specify the page size or the page token from which to start listing subscriptions. + * + *

    Example of listing subscriptions for a topic, specifying the page size. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * Page subscriptions =
    +   *     pubsub.listSubscriptions(topicName, ListOption.pageSize(100));
    +   * Iterator subscriptionIterator = subscriptions.iterateAll();
    +   * while (subscriptionIterator.hasNext()) {
    +   *   SubscriptionId subscription = subscriptionIterator.next();
    +   *   // do something with the subscription identity
    +   * }
    +   * }
    + * + * @param topic the topic for which to list subscriptions + * @throws PubSubException upon failure + */ + Page listSubscriptions(String topic, ListOption... options); + + /** + * Sends a request for listing the identities of subscriptions for the provided topic. This method + * returns a {@code Future} object to consume the result. {@link Future#get()} returns an + * {@link AsyncPage} object that can be used to asynchronously handle paginated results. Use + * {@link ListOption} to specify the page size or the page token from which to start listing + * subscriptions. + * + *

    Example of asynchronously listing subscriptions for a topic, specifying the page size. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * Future> future =
    +   *     pubsub.listSubscriptionsAsync(topicName, ListOption.pageSize(100));
    +   * // ...
    +   * AsyncPage subscriptions = future.get();
    +   * Iterator subscriptionIterator = subscriptions.iterateAll();
    +   * while (subscriptionIterator.hasNext()) {
    +   *   SubscriptionId subscription = subscriptionIterator.next();
    +   *   // do something with the subscription identity
    +   * }
    +   * }
    + * + * @param topic the topic for which to list subscriptions + */ + Future> listSubscriptionsAsync(String topic, ListOption... options); + + /** + * Pulls messages from the provided subscription. This method possibly returns no messages if no + * message was available at the time the request was processed by the Pub/Sub service (i.e. the + * system is not allowed to wait until at least one message is available - + * return_immediately + * option is set to {@code true}). Pulled messages have their acknowledge deadline automatically + * renewed until they are explicitly consumed using {@link Iterator#next()}. + * + *

    Example of pulling a maximum number of messages from a subscription. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * Iterator messages = pubsub.pull(subscriptionName, 100);
    +   * // Ack deadline is renewed until the message is consumed
    +   * while (messages.hasNext()) {
    +   *   ReceivedMessage message = messages.next();
    +   *   // do something with message and ack/nack it
    +   *   message.ack(); // or message.nack()
    +   * }
    +   * }
    + * + * @param subscription the subscription from which to pull messages + * @param maxMessages the maximum number of messages pulled by this method. This method can + * possibly return fewer messages. + * @throws PubSubException upon failure + */ + Iterator pull(String subscription, int maxMessages); + + /** + * Sends a request for pulling messages from the provided subscription. This method returns a + * {@code Future} object to consume the result. {@link Future#get()} returns a message iterator. + * When using this method the system is allowed to wait until at least one message is available + * rather than returning no messages (i.e. + * return_immediately + * option is set to {@code false}). The client may cancel the request by calling + * {@link Future#cancel(boolean)} if it does not wish to wait any longer. Notice that the Pub/Sub + * service might still return no messages if a timeout is reached on the service side. + * + *

    Example of asynchronously pulling a maximum number of messages from a subscription. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * Future> future = pubsub.pullAsync(subscriptionName, 100);
    +   * // ...
    +   * Iterator messages = future.get();
    +   * // Ack deadline is renewed until the message is consumed
    +   * while (messages.hasNext()) {
    +   *   ReceivedMessage message = messages.next();
    +   *   // do something with message and ack/nack it
    +   *   message.ack(); // or message.nack()
    +   * }
    +   * }
    + * + * @param subscription the subscription from which to pull messages + * @param maxMessages the maximum number of messages pulled by this method. This method can + * possibly return fewer messages. + * @throws PubSubException upon failure + */ + Future> pullAsync(String subscription, int maxMessages); + + /** + * Creates a message consumer that pulls messages from the provided subscription. You can stop + * pulling messages by calling {@link MessageConsumer#close()}. The returned message consumer + * executes {@link MessageProcessor#process(Message)} on each pulled message. If + * {@link MessageProcessor#process(Message)} executes correctly, the message is acknowledged. If + * {@link MessageProcessor#process(Message)} throws an exception, the message is "nacked". For + * all pulled messages, the ack deadline is automatically renewed until the message is either + * acknowledged or "nacked". + * + *

    The {@link PullOption#maxQueuedCallbacks(int)} option can be used to control the maximum + * number of queued messages (messages either being processed or waiting to be processed). The + * {@link PullOption#executorFactory(ExecutorFactory)} can be used to provide an executor to run + * message processor callbacks. + * + *

    Example of continuously pulling messages from a subscription. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * MessageProcessor callback = new MessageProcessor() {
    +   *   public void process(Message message) throws Exception {
    +   *     // Ack deadline is renewed until this method returns
    +   *     // Message is acked if this method returns successfully
    +   *     // Message is nacked if this method throws an exception
    +   *   }
    +   * };
    +   * PubSub.MessageConsumer consumer = pubsub.pullAsync(subscriptionName, callback);
    +   * // ...
    +   * // Stop pulling
    +   * consumer.close();
    +   * }
    + * + * @param subscription the subscription from which to pull messages + * @param callback the callback to be executed on each message + * @param options pulling options + * @return a message consumer for the provided subscription and options + */ + MessageConsumer pullAsync(String subscription, MessageProcessor callback, PullOption... options); + + /** + * Acknowledges the given messages for the provided subscription. Ack ids identify the messages to + * acknowledge, as returned in {@link ReceivedMessage#ackId()} by {@link #pull(String, int)} and + * {@link #pullAsync(String, int)}. + * + *

    Example of acking one message. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId = "message_ack_id";
    +   * pubsub.ack(subscriptionName, ackId);
    +   * }
    + * + *

    Example of acking more messages. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId1 = "message1_ack_id";
    +   * String ackId2 = "message2_ack_id";
    +   * pubsub.ack(subscriptionName, ackId1, ackId2);
    +   * }
    + * + * @param subscription the subscription whose messages must be acknowledged + * @param ackId the ack id of the first message to acknowledge + * @param ackIds other ack ids of messages to acknowledge + * @throws PubSubException upon failure, or if the subscription was not found + */ + void ack(String subscription, String ackId, String... ackIds); + + /** + * Sends a request to acknowledge the given messages for the provided subscription. Ack ids + * identify the messages to acknowledge, as returned in {@link ReceivedMessage#ackId()} by + * {@link #pull(String, int)} and {@link #pullAsync(String, int)}. The method returns a + * {@code Future} object that can be used to wait for the acknowledge operation to be completed. + * + *

    Example of asynchronously acking one message. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId = "message_ack_id";
    +   * Future future = pubsub.ackAsync(subscriptionName, ackId);
    +   * // ...
    +   * future.get();
    +   * }
    + * + *

    Example of asynchronously acking more messages. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId1 = "message1_ack_id";
    +   * String ackId2 = "message2_ack_id";
    +   * Future future = pubsub.ackAsync(subscriptionName, ackId1, ackId2);
    +   * // ...
    +   * future.get();
    +   * }
    + * + * @param subscription the subscription whose messages must be acknowledged + * @param ackId the ack id of the first message to acknowledge + * @param ackIds other ack ids of messages to acknowledge + */ + Future ackAsync(String subscription, String ackId, String... ackIds); + + /** + * Acknowledges the given messages for the provided subscription. Ack ids identify the messages to + * acknowledge, as returned in {@link ReceivedMessage#ackId()} by {@link #pull(String, int)} and + * {@link #pullAsync(String, int)}. + * + *

    Example of acking a list of messages. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId1 = "message1_ack_id";
    +   * String ackId2 = "message2_ack_id";
    +   * List ackIds = new LinkedList<>();
    +   * ackIds.add(ackId1);
    +   * ackIds.add(ackId2);
    +   * pubsub.ack(subscriptionName, ackIds);
    +   * }
    + * + * @param subscription the subscription whose messages must be acknowledged + * @param ackIds the ack ids of messages to acknowledge + * @throws PubSubException upon failure, or if the subscription was not found + */ + void ack(String subscription, Iterable ackIds); + + /** + * Sends a request to acknowledge the given messages for the provided subscription. Ack ids + * identify the messages to acknowledge, as returned in {@link ReceivedMessage#ackId()} by + * {@link #pull(String, int)} and {@link #pullAsync(String, int)}. The method returns a + * {@code Future} object that can be used to wait for the acknowledge operation to be completed. + * + *

    Example of asynchronously acking a list of messages. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId1 = "message1_ack_id";
    +   * String ackId2 = "message2_ack_id";
    +   * List ackIds = new LinkedList<>();
    +   * ackIds.add(ackId1);
    +   * ackIds.add(ackId2);
    +   * Future future = pubsub.ackAsync(subscriptionName, ackIds);
    +   * // ...
    +   * future.get();
    +   * }
    + * + * @param subscription the subscription whose messages must be acknowledged + * @param ackIds the ack ids of messages to acknowledge + */ + Future ackAsync(String subscription, Iterable ackIds); + + /** + * "Nacks" the given messages for the provided subscription. Ack ids identify the messages to + * "nack", as returned in {@link ReceivedMessage#ackId()} by {@link #pull(String, int)} and + * {@link #pullAsync(String, int)}. This method corresponds to calling + * {@link #modifyAckDeadline(String, int, TimeUnit, String, String...)} with a deadline of 0. + * + *

    Example of nacking one message. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId = "message_ack_id";
    +   * pubsub.nack(subscriptionName, ackId);
    +   * }
    + * + *

    Example of nacking more messages. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId1 = "message1_ack_id";
    +   * String ackId2 = "message2_ack_id";
    +   * pubsub.nack(subscriptionName, ackId1, ackId2);
    +   * }
    + * + * @param subscription the subscription whose messages must be "nacked" + * @param ackId the ack id of the first message to "nack" + * @param ackIds other ack ids of messages to "nack" + * @throws PubSubException upon failure, or if the subscription was not found + */ + void nack(String subscription, String ackId, String... ackIds); + + /** + * Sends a request to "nack" the given messages for the provided subscription. Ack ids identify + * the messages to "nack", as returned in {@link ReceivedMessage#ackId()} by + * {@link #pull(String, int)} and {@link #pullAsync(String, int)}. This method corresponds to + * calling {@link #modifyAckDeadlineAsync(String, int, TimeUnit, String, String...)} with a + * deadline of 0. The method returns a {@code Future} object that can be used to wait for the + * "nack" operation to be completed. + * + *

    Example of asynchronously nacking one message. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId = "message_ack_id";
    +   * Future future = pubsub.nackAsync(subscriptionName, ackId);
    +   * // ...
    +   * future.get();
    +   * }
    + * + *

    Example of asynchronously nacking more messages. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId1 = "message1_ack_id";
    +   * String ackId2 = "message2_ack_id";
    +   * Future future = pubsub.nackAsync(subscriptionName, ackId1, ackId2);
    +   * // ...
    +   * future.get();
    +   * }
    + * + * @param subscription the subscription whose messages must be "nacked" + * @param ackId the ack id of the first message to "nack" + * @param ackIds other ack ids of messages to "nack" + */ + Future nackAsync(String subscription, String ackId, String... ackIds); + + /** + * "Nacks" the given messages for the provided subscription. Ack ids identify the messages to + * "nack", as returned in {@link ReceivedMessage#ackId()} by {@link #pull(String, int)} and + * {@link #pullAsync(String, int)}. This method corresponds to calling + * {@link #modifyAckDeadline(String, int, TimeUnit, Iterable)} with a deadline of 0. + * + *

    Example of nacking a list of messages. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId1 = "message1_ack_id";
    +   * String ackId2 = "message2_ack_id";
    +   * List ackIds = new LinkedList<>();
    +   * ackIds.add(ackId1);
    +   * ackIds.add(ackId2);
    +   * pubsub.nack(subscriptionName, ackIds);
    +   * }
    + * + * @param subscription the subscription whose messages must be "nacked" + * @param ackIds the ack ids of messages to "nack" + * @throws PubSubException upon failure, or if the subscription was not found + */ + void nack(String subscription, Iterable ackIds); + + /** + * Sends a request to "nack" the given messages for the provided subscription. Ack ids identify + * the messages to "nack", as returned in {@link ReceivedMessage#ackId()} by + * {@link #pull(String, int)} and {@link #pullAsync(String, int)}. This method corresponds to + * calling {@link #modifyAckDeadlineAsync(String, int, TimeUnit, Iterable)} with a deadline of 0. + * The method returns a {@code Future} object that can be used to wait for the "nack" operation to + * be completed. + * + *

    Example of asynchronously nacking a list of messages. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId1 = "message1_ack_id";
    +   * String ackId2 = "message2_ack_id";
    +   * List ackIds = new LinkedList<>();
    +   * ackIds.add(ackId1);
    +   * ackIds.add(ackId2);
    +   * Future future = pubsub.nackAsync(subscriptionName, ackIds);
    +   * // ...
    +   * future.get();
    +   * }
    + * + * @param subscription the subscription whose messages must be "nacked" + * @param ackIds the ack ids of messages to "nack" + */ + Future nackAsync(String subscription, Iterable ackIds); + + /** + * Modifies the acknowledge deadline of the given messages. {@code deadline} must be >= 0 and + * is the new deadline with respect to the time the modify request was received by the Pub/Sub + * service. For example, if {@code deadline} is 10 and {@code unit} is {@link TimeUnit#SECONDS}, + * the new ack deadline will expire 10 seconds after the modify request was received by the + * service. Specifying 0 may be used to make the message available for another pull request + * (corresponds to calling {@link #nack(String, String, String...)}). + * + *

    Example of modifying the ack deadline of one message. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId = "message_ack_id";
    +   * pubsub.modifyAckDeadline(subscriptionName, 60, TimeUnit.SECONDS, ackId);
    +   * }
    + * + *

    Example of modifying the ack deadline of some messages. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId1 = "message1_ack_id";
    +   * String ackId2 = "message2_ack_id";
    +   * pubsub.modifyAckDeadline(subscriptionName, 60, TimeUnit.SECONDS, ackId1, ackId2);
    +   * }
    + * + * @param subscription the subscription whose messages need to update their acknowledge deadline + * @param deadline the new deadline, relative to the time the modify request is received by the + * Pub/Sub service + * @param unit time unit for the {@code deadline} parameter + * @param ackId the ack id of the first message for which the acknowledge deadline must be + * modified + * @param ackIds other ack ids of messages for which the acknowledge deadline must be modified + * @throws PubSubException upon failure, or if the subscription was not found + */ + void modifyAckDeadline(String subscription, int deadline, TimeUnit unit, String ackId, + String... ackIds); + + /** + * Sends a request to modify the acknowledge deadline of the given messages. {@code deadline} + * must be >= 0 and is the new deadline with respect to the time the modify request was + * received by the Pub/Sub service. For example, if {@code deadline} is 10 and {@code unit} is + * {@link TimeUnit#SECONDS}, the new ack deadline will expire 10 seconds after the modify request + * was received by the service. Specifying 0 may be used to make the message available for another + * pull request (corresponds to calling {@link #nackAsync(String, Iterable)}). The method returns + * a {@code Future} object that can be used to wait for the modify operation to be completed. + * + *

    Example of asynchronously modifying the ack deadline of one message. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId = "message_ack_id";
    +   * Future future =
    +   *     pubsub.modifyAckDeadlineAsync(subscriptionName, 60, TimeUnit.SECONDS, ackId);
    +   * // ...
    +   * future.get();
    +   * }
    + * + *

    Example of asynchronously modifying the ack deadline of some messages. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId1 = "message1_ack_id";
    +   * String ackId2 = "message2_ack_id";
    +   * Future future =
    +   *     pubsub.modifyAckDeadlineAsync(subscriptionName, 60, TimeUnit.SECONDS, ackId1, ackId2);
    +   * // ...
    +   * future.get();
    +   * }
    + * + * @param subscription the subscription whose messages need to update their acknowledge deadline + * @param deadline the new deadline, relative to the time the modify request is received by the + * Pub/Sub service + * @param unit time unit for the {@code deadline} parameter + * @param ackId the ack id of the first message for which the acknowledge deadline must be + * modified + * @param ackIds other ack ids of messages for which the acknowledge deadline must be modified + */ + Future modifyAckDeadlineAsync(String subscription, int deadline, TimeUnit unit, + String ackId, String... ackIds); + + /** + * Modifies the acknowledge deadline of the given messages. {@code deadline} must be >= 0 and + * is the new deadline with respect to the time the modify request was received by the Pub/Sub + * service. For example, if {@code deadline} is 10 and {@code unit} is {@link TimeUnit#SECONDS}, + * the new ack deadline will expire 10 seconds after the modify request was received by the + * service. Specifying 0 may be used to make the message available for another pull request + * (corresponds to calling {@link #nack(String, Iterable)}). + * + *

    Example of modifying the ack deadline of a list of messages. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId1 = "message1_ack_id";
    +   * String ackId2 = "message2_ack_id";
    +   * List ackIds = new LinkedList<>();
    +   * ackIds.add(ackId1);
    +   * ackIds.add(ackId2);
    +   * pubsub.modifyAckDeadline(subscriptionName, 60, TimeUnit.SECONDS, ackIds);
    +   * }
    + * + * @param subscription the subscription whose messages need to update their acknowledge deadline + * @param deadline the new deadline, relative to the time the modify request is received by the + * Pub/Sub service + * @param unit time unit for the {@code deadline} parameter + * @param ackIds the ack ids of messages for which the acknowledge deadline must be modified + * @throws PubSubException upon failure, or if the subscription was not found + */ + void modifyAckDeadline(String subscription, int deadline, TimeUnit unit, Iterable ackIds); + + /** + * Sends a request to modify the acknowledge deadline of the given messages. {@code deadline} + * must be >= 0 and is the new deadline with respect to the time the modify request was + * received by the Pub/Sub service. For example, if {@code deadline} is 10 and {@code unit} is + * {@link TimeUnit#SECONDS}, the new ack deadline will expire 10 seconds after the modify request + * was received by the service. Specifying 0 may be used to make the message available for another + * pull request (corresponds to calling {@link #nackAsync(String, Iterable)}). The method returns + * a {@code Future} object that can be used to wait for the modify operation to be completed. + * + *

    Example of asynchronously modifying the ack deadline of a list of messages. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * String ackId1 = "message1_ack_id";
    +   * String ackId2 = "message2_ack_id";
    +   * List ackIds = new LinkedList<>();
    +   * ackIds.add(ackId1);
    +   * ackIds.add(ackId2);
    +   * Future future =
    +   *     pubsub.modifyAckDeadlineAsync(subscriptionName, 60, TimeUnit.SECONDS, ackIds);
    +   * // ...
    +   * future.get();
    +   * }
    + * + * @param subscription the subscription whose messages need to update their acknowledge deadline + * @param deadline the new deadline, relative to the time the modify request is received by the + * Pub/Sub service + * @param unit time unit for the {@code deadline} parameter + * @param ackIds the ack ids of messages for which the acknowledge deadline must be modified + */ + Future modifyAckDeadlineAsync(String subscription, int deadline, TimeUnit unit, + Iterable ackIds); + + /** + * Returns the IAM access control policy for the specified topic. Returns {@code null} if the + * topic was not found. + * + *

    Example of getting a topic policy. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * Policy policy = pubsub.getTopicPolicy(topicName);
    +   * if (policy == null) {
    +   *   // topic was not found
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + Policy getTopicPolicy(String topic); + + /** + * Sends a request for getting the IAM access control policy for the specified topic. This method + * returns a {@code Future} object to consume the result. {@link Future#get()} returns the + * requested policy or {@code null} if the topic was not found. + * + *

    Example of asynchronously getting a topic policy. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * Future future = pubsub.getTopicPolicyAsync(topicName);
    +   * // ...
    +   * Policy policy = future.get();
    +   * if (policy == null) {
    +   *   // topic was not found
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + Future getTopicPolicyAsync(String topic); + + /** + * Sets the IAM access control policy for the specified topic. Replaces any existing policy. This + * method returns the new policy. + * + *

    It is recommended that you use the read-modify-write pattern. This pattern entails reading + * the project's current policy, updating it locally, and then sending the modified policy for + * writing. Cloud IAM solves the problem of conflicting processes simultaneously attempting to + * modify a policy by using the {@link Policy#etag etag} property. This property is used to + * verify whether the policy has changed since the last request. When you make a request with an + * etag value, the value in the request is compared with the existing etag value associated with + * the policy. The policy is written only if the etag values match. If the etags don't match, a + * {@code PubSubException} is thrown, denoting that the server aborted update. If an etag is not + * provided, the policy is overwritten blindly. + * + *

    Example of replacing a topic policy. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * Policy policy = pubsub.getTopicPolicy(topicName);
    +   * Policy updatedPolicy = policy.toBuilder()
    +   *     .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers())
    +   *     .build();
    +   * updatedPolicy = pubsub.replaceTopicPolicy(topicName, updatedPolicy);
    +   * }
    + * + * @throws PubSubException upon failure + */ + Policy replaceTopicPolicy(String topic, Policy newPolicy); + + /** + * Sends a request to set the IAM access control policy for the specified topic. Replaces any + * existing policy. This method returns a {@code Future} object to consume the result. + * {@link Future#get()} returns the new policy. + * + *

    It is recommended that you use the read-modify-write pattern. This pattern entails reading + * the project's current policy, updating it locally, and then sending the modified policy for + * writing. Cloud IAM solves the problem of conflicting processes simultaneously attempting to + * modify a policy by using the {@link Policy#etag etag} property. This property is used to + * verify whether the policy has changed since the last request. When you make a request with an + * etag value, the value in the request is compared with the existing etag value associated with + * the policy. The policy is written only if the etag values match. If the etags don't match, + * {@link Future#get()} will throw a {@link java.util.concurrent.ExecutionException} caused by a + * {@code PubSubException}, denoting that the server aborted update. If an etag is not provided, + * the policy is overwritten blindly. + * + *

    Example of asynchronously replacing a topic policy. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * Policy policy = pubsub.getTopicPolicy(topicName);
    +   * Policy updatedPolicy = policy.toBuilder()
    +   *     .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers())
    +   *     .build();
    +   * Future future = pubsub.replaceTopicPolicyAsync(topicName, updatedPolicy);
    +   * // ...
    +   * updatedPolicy = future.get();
    +   * }
    + * + * @throws PubSubException upon failure + */ + Future replaceTopicPolicyAsync(String topic, Policy newPolicy); + + /** + * Returns the permissions that a caller has on the specified topic. + * + *

    You typically don't call this method if you're using Google Cloud Platform directly to + * manage permissions. This method is intended for integration with your proprietary software, + * such as a customized graphical user interface. For example, the Cloud Platform Console tests + * IAM permissions internally to determine which UI should be available to the logged-in user. + * + *

    Example of testing whether the caller has the provided permissions on a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * List permissions = new LinkedList<>();
    +   * permissions.add("pubsub.topics.get");
    +   * List testedPermissions = pubsub.testTopicPermissions(topicName, permissions);
    +   * }
    + * + * @return A list of booleans representing whether the caller has the permissions specified (in + * the order of the given permissions) + * @throws PubSubException upon failure + * @see + * Permissions and Roles + */ + List testTopicPermissions(String topic, List permissions); + + /** + * Sends a request to get the permissions that a caller has on the specified topic. + * + *

    You typically don't call this method if you're using Google Cloud Platform directly to + * manage permissions. This method is intended for integration with your proprietary software, + * such as a customized graphical user interface. For example, the Cloud Platform Console tests + * IAM permissions internally to determine which UI should be available to the logged-in user. + * + *

    Example of asynchronously testing whether the caller has the provided permissions on a topic. + *

     {@code
    +   * String topicName = "my_topic_name";
    +   * List permissions = new LinkedList<>();
    +   * permissions.add("pubsub.topics.get");
    +   * Future> future = pubsub.testTopicPermissionsAsync(topicName, permissions);
    +   * // ...
    +   * List testedPermissions = future.get();
    +   * }
    + * + * @return A {@code Future} object to consume the result. {@link Future#get()} returns a list of + * booleans representing whether the caller has the permissions specified (in the order of the + * given permissions) + * @throws PubSubException upon failure + * @see + * Permissions and Roles + */ + Future> testTopicPermissionsAsync(String topic, List permissions); + + /** + * Returns the IAM access control policy for the specified subscription. Returns {@code null} if + * the subscription was not found. + * + *

    Example of getting a subscription policy. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * Policy policy = pubsub.getSubscriptionPolicy(subscriptionName);
    +   * if (policy == null) {
    +   *   // subscription was not found
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + Policy getSubscriptionPolicy(String subscription); + + /** + * Sends a request for getting the IAM access control policy for the specified subscription. This + * method returns a {@code Future} object to consume the result. {@link Future#get()} returns the + * requested policy or {@code null} if the subscription was not found. + * + *

    Example of asynchronously getting a subscription policy. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * Future future = pubsub.getSubscriptionPolicyAsync(subscriptionName);
    +   * // ...
    +   * Policy policy = future.get();
    +   * if (policy == null) {
    +   *   // subscription was not found
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + Future getSubscriptionPolicyAsync(String subscription); + + /** + * Sets the IAM access control policy for the specified subscription. Replaces any existing + * policy. This method returns the new policy. + * + *

    It is recommended that you use the read-modify-write pattern. This pattern entails reading + * the project's current policy, updating it locally, and then sending the modified policy for + * writing. Cloud IAM solves the problem of conflicting processes simultaneously attempting to + * modify a policy by using the {@link Policy#etag etag} property. This property is used to + * verify whether the policy has changed since the last request. When you make a request with an + * etag value, the value in the request is compared with the existing etag value associated with + * the policy. The policy is written only if the etag values match. If the etags don't match, a + * {@code PubSubException} is thrown, denoting that the server aborted update. If an etag is not + * provided, the policy is overwritten blindly. + * + *

    Example of replacing a subscription policy. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * Policy policy = pubsub.getSubscriptionPolicy(subscriptionName);
    +   * Policy updatedPolicy = policy.toBuilder()
    +   *     .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers())
    +   *     .build();
    +   * updatedPolicy = pubsub.replaceSubscriptionPolicy(subscriptionName, updatedPolicy);
    +   * }
    + * + * @throws PubSubException upon failure + */ + Policy replaceSubscriptionPolicy(String subscription, Policy newPolicy); + + /** + * Sends a request to set the IAM access control policy for the specified subscription. Replaces + * any existing policy. This method returns a {@code Future} object to consume the result. + * {@link Future#get()} returns the new policy. + * + *

    It is recommended that you use the read-modify-write pattern. This pattern entails reading + * the project's current policy, updating it locally, and then sending the modified policy for + * writing. Cloud IAM solves the problem of conflicting processes simultaneously attempting to + * modify a policy by using the {@link Policy#etag etag} property. This property is used to + * verify whether the policy has changed since the last request. When you make a request with an + * etag value, the value in the request is compared with the existing etag value associated with + * the policy. The policy is written only if the etag values match. If the etags don't match, + * {@link Future#get()} will throw a {@link java.util.concurrent.ExecutionException} caused by a + * {@code PubSubException}, denoting that the server aborted update. If an etag is not provided, + * the policy is overwritten blindly. + * + *

    Example of asynchronously replacing a subscription policy. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * Policy policy = pubsub.getSubscriptionPolicy(subscriptionName);
    +   * Policy updatedPolicy = policy.toBuilder()
    +   *     .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers())
    +   *     .build();
    +   * Future future =
    +   *     pubsub.replaceSubscriptionPolicyAsync(subscriptionName, updatedPolicy);
    +   * // ...
    +   * updatedPolicy = future.get();
    +   * }
    + * + * @throws PubSubException upon failure + */ + Future replaceSubscriptionPolicyAsync(String subscription, Policy newPolicy); + + /** + * Returns the permissions that a caller has on the specified subscription. You typically don't + * call this method if you're using Google Cloud Platform directly to manage permissions. This + * method is intended for integration with your proprietary software, such as a customized + * graphical user interface. For example, the Cloud Platform Console tests IAM permissions + * internally to determine which UI should be available to the logged-in user. + * + *

    Example of testing whether the caller has the provided permissions on a subscription. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * List permissions = new LinkedList<>();
    +   * permissions.add("pubsub.subscriptions.get");
    +   * List testedPermissions =
    +   *     pubsub.testSubscriptionPermissions(subscriptionName, permissions);
    +   * }
    + * + * @return A list of booleans representing whether the caller has the permissions specified (in + * the order of the given permissions) + * @throws PubSubException upon failure + * @see + * Permissions and Roles + */ + List testSubscriptionPermissions(String subscription, List permissions); + + /** + * Sends a request to get the permissions that a caller has on the specified subscription. + * + *

    You typically don't call this method if you're using Google Cloud Platform directly to + * manage permissions. This method is intended for integration with your proprietary software, + * such as a customized graphical user interface. For example, the Cloud Platform Console tests + * IAM permissions internally to determine which UI should be available to the logged-in user. + * + *

    Example of asynchronously testing whether the caller has the provided permissions on a + * subscription. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * List permissions = new LinkedList<>();
    +   * permissions.add("pubsub.subscriptions.get");
    +   * Future> future =
    +   *     pubsub.testSubscriptionPermissionsAsync(subscriptionName, permissions);
    +   * // ...
    +   * List testedPermissions = future.get();
    +   * }
    + * + * @return A {@code Future} object to consume the result. {@link Future#get()} returns a list of + * booleans representing whether the caller has the permissions specified (in the order of the + * given permissions) + * @throws PubSubException upon failure + * @see + * Permissions and Roles + */ + Future> testSubscriptionPermissionsAsync(String subscription, + List permissions); +} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubException.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubException.java new file mode 100644 index 000000000000..fa18d85e2eeb --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubException.java @@ -0,0 +1,46 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import com.google.api.gax.grpc.ApiException; +import com.google.cloud.BaseServiceException; + +import java.io.IOException; +import java.util.Set; + +/** + * Pub/Sub service exception. + * + * @see Google Cloud Pub/Sub error codes + */ +public final class PubSubException extends BaseServiceException { + + private static final long serialVersionUID = 6434989638600001226L; + + public PubSubException(IOException ex, boolean idempotent) { + super(ex, idempotent); + } + + public PubSubException(ApiException apiException, boolean idempotent) { + super(apiException, idempotent); + } + + @Override + protected Set getRetryableErrors() { + return null; + } +} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubFactory.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubFactory.java new file mode 100644 index 000000000000..38f922dbce3b --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubFactory.java @@ -0,0 +1,24 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import com.google.cloud.ServiceFactory; + +/** + * An interface for Pub/Sub factories. + */ +public interface PubSubFactory extends ServiceFactory {} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubOptions.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubOptions.java new file mode 100644 index 000000000000..ac8262d1af97 --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PubSubOptions.java @@ -0,0 +1,144 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import com.google.cloud.GrpcServiceOptions; +import com.google.cloud.pubsub.spi.DefaultPubSubRpc; +import com.google.cloud.pubsub.spi.PubSubRpc; +import com.google.cloud.pubsub.spi.PubSubRpcFactory; +import com.google.cloud.pubsub.spi.v1.PublisherSettings; +import com.google.common.collect.ImmutableSet; + +import java.io.IOException; +import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; + +public class PubSubOptions extends GrpcServiceOptions { + + private static final long serialVersionUID = 5598666986447361352L; + private static final String PUBSUB_SCOPE = "https://www.googleapis.com/auth/pubsub"; + private static final Set SCOPES = ImmutableSet.of(PUBSUB_SCOPE); + private static final String EMULATOR_HOST_ENV_VAR = "PUBSUB_EMULATOR_HOST"; + private static final String DEFAULT_HOST = PublisherSettings.getDefaultServiceAddress() + + ':' + PublisherSettings.getDefaultServicePort(); + + public static class DefaultPubSubFactory implements PubSubFactory { + private static final PubSubFactory INSTANCE = new DefaultPubSubFactory(); + + @Override + public PubSub create(PubSubOptions options) { + return new PubSubImpl(options); + } + } + + /** + * Returns a default {@code PubSubOptions} instance. + */ + @Deprecated + public static PubSubOptions defaultInstance() { + return getDefaultInstance(); + } + + /** + * Returns a default {@code PubSubOptions} instance. + */ + public static PubSubOptions getDefaultInstance() { + return newBuilder().build(); + } + + public static class DefaultPubSubRpcFactory implements PubSubRpcFactory { + private static final PubSubRpcFactory INSTANCE = new DefaultPubSubRpcFactory(); + + @Override + public PubSubRpc create(PubSubOptions options) { + try { + return new DefaultPubSubRpc(options); + } catch (IOException e) { + throw new PubSubException(e, true); + } + } + } + + @Override + protected String getDefaultHost() { + String host = System.getProperty(EMULATOR_HOST_ENV_VAR, System.getenv(EMULATOR_HOST_ENV_VAR)); + return host != null ? host : DEFAULT_HOST; + } + + public static class Builder extends + GrpcServiceOptions.Builder { + + private Builder() {} + + private Builder(PubSubOptions options) { + super(options); + } + + @Override + public PubSubOptions build() { + return new PubSubOptions(this); + } + } + + protected PubSubOptions(Builder builder) { + super(PubSubFactory.class, PubSubRpcFactory.class, builder); + } + + @Override + protected ExecutorFactory getExecutorFactory() { + return super.getExecutorFactory(); + } + + @Override + protected PubSubFactory getDefaultServiceFactory() { + return DefaultPubSubFactory.INSTANCE; + } + + @Override + protected PubSubRpcFactory getDefaultRpcFactory() { + return DefaultPubSubRpcFactory.INSTANCE; + } + + @Override + protected Set getScopes() { + return SCOPES; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof PubSubOptions && baseEquals((PubSubOptions) obj); + } + + @Override + public int hashCode() { + return baseHashCode(); + } + + @Override + public Builder toBuilder() { + return new Builder(this); + } + + @Deprecated + public static Builder builder() { + return newBuilder(); + } + + public static Builder newBuilder() { + return new Builder(); + } +} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PushConfig.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PushConfig.java new file mode 100644 index 000000000000..cd5c4aa06cda --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/PushConfig.java @@ -0,0 +1,363 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableMap; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Google Cloud Pub/Sub configuration for a push subscription. + * + *

    In a push subscription, the Pub/Sub server sends a request to the subscriber application. A + * {@code PushConfig} object can be used to configure the application endpoint. The subscriber's + * HTTP response serves as an implicit acknowledgement: a success response indicates that the + * message has been succesfully processed and the Pub/Sub system can delete it from the + * subscription; a non-success response indicates that the Pub/Sub server should resend it + * (implicit "nack"). + * + * @see Subscriber Guide + */ +public final class PushConfig implements Serializable { + + private static final long serialVersionUID = 4408885787064092231L; + + private final String endpoint; + private final ImmutableMap attributes; + + /** + * Builder for {@code PushConfig} objects. + */ + public static final class Builder { + + private String endpoint; + private Map attributes = new HashMap<>(); + + private Builder() { + } + + /** + * Sets the URL locating the endpoint to which messages should be pushed. For example, an + * endpoint might use {@code https://example.com/push}. + */ + @Deprecated + public Builder endpoint(String endpoint) { + return setEndpoint(endpoint); + } + + /** + * Sets the URL locating the endpoint to which messages should be pushed. For example, an + * endpoint might use {@code https://example.com/push}. + */ + public Builder setEndpoint(String endpoint) { + this.endpoint = checkNotNull(endpoint); + return this; + } + + /** + * Adds an API-supported attribute that can be used to control different aspects of the message + * delivery. + * + *

    The currently supported attribute is {@code x-goog-version}, which can be used to change + * the format of the push message. This attribute indicates the version of the data expected by + * the endpoint. The endpoint version is based on the version of the Pub/Sub API. Possible + * values for this attribute are: + *

      + *
    • {@code v1beta1}: uses the push format defined in the v1beta1 Pub/Sub API + *
    • {@code v1} or {@code v1beta2}: uses the push format defined in the v1 Pub/Sub API + *
    + * + *

    If the {@code x-goog-version} attribute is not present when a subscription is created (see + * {@link PubSub#create(SubscriptionInfo)} and {@link PubSub#createAsync(SubscriptionInfo)}), it + * will default to {@code v1}. If it is not present when modifying the push config (see + * {@link PubSub#replacePushConfig(String, PushConfig)} and + * {@link PubSub#replacePushConfigAsync(String, PushConfig)}), its value will not be changed. + * + * @see Message Format + */ + public Builder addAttribute(String name, String value) { + attributes.put(name, value); + return this; + } + + /** + * Sets the API-supported attributes that can be used to control different aspects of the + * message delivery. + * + *

    The currently supported attribute is {@code x-goog-version}, which can be used to change + * the format of the push message. This attribute indicates the version of the data expected by + * the endpoint. The endpoint version is based on the version of the Pub/Sub API. Possible + * values for this attribute are: + *

      + *
    • {@code v1beta1}: uses the push format defined in the v1beta1 Pub/Sub API + *
    • {@code v1} or {@code v1beta2}: uses the push format defined in the v1 Pub/Sub API + *
    + * + *

    If the {@code x-goog-version} attribute is not present when a subscription is created (see + * {@link PubSub#create(SubscriptionInfo)} and {@link PubSub#createAsync(SubscriptionInfo)}), it + * will default to {@code v1}. If it is not present when modifying the push config (see + * {@link PubSub#replacePushConfig(String, PushConfig)} and + * {@link PubSub#replacePushConfigAsync(String, PushConfig)}), its value will not be changed. + * + * @see Message Format + */ + @Deprecated + public Builder attributes(Map attributes) { + return setAttributes(attributes); + } + + /** + * Sets the API-supported attributes that can be used to control different aspects of the + * message delivery. + * + *

    The currently supported attribute is {@code x-goog-version}, which can be used to change + * the format of the push message. This attribute indicates the version of the data expected by + * the endpoint. The endpoint version is based on the version of the Pub/Sub API. Possible + * values for this attribute are: + *

      + *
    • {@code v1beta1}: uses the push format defined in the v1beta1 Pub/Sub API + *
    • {@code v1} or {@code v1beta2}: uses the push format defined in the v1 Pub/Sub API + *
    + * + *

    If the {@code x-goog-version} attribute is not present when a subscription is created (see + * {@link PubSub#create(SubscriptionInfo)} and {@link PubSub#createAsync(SubscriptionInfo)}), it + * will default to {@code v1}. If it is not present when modifying the push config (see + * {@link PubSub#replacePushConfig(String, PushConfig)} and + * {@link PubSub#replacePushConfigAsync(String, PushConfig)}), its value will not be changed. + * + * @see Message Format + */ + public Builder setAttributes(Map attributes) { + this.attributes = new HashMap<>(attributes); + return this; + } + + /** + * Removes an API-supported attribute. + */ + public Builder removeAttribute(String name) { + attributes.remove(name); + return this; + } + + /** + * Clears all API-supported attributes. + */ + public Builder clearAttributes() { + attributes.clear(); + return this; + } + + /** + * Creates a {@code PushConfig} object. + */ + public PushConfig build() { + return new PushConfig(this); + } + } + + private PushConfig(Builder builder) { + endpoint = builder.endpoint; + attributes = ImmutableMap.copyOf(builder.attributes); + } + + /** + * Returns the URL locating the endpoint to which messages should be pushed. For example, an + * endpoint might use {@code https://example.com/push}. + */ + @Deprecated + public String endpoint() { + return getEndpoint(); + } + + /** + * Returns the URL locating the endpoint to which messages should be pushed. For example, an + * endpoint might use {@code https://example.com/push}. + */ + public String getEndpoint() { + return endpoint; + } + + /** + * Returns the API-supported attributes that can be used to control different aspects of the + * message delivery. + * + *

    The currently supported attribute is {@code x-goog-version}, which can be used to change + * the format of the push message. This attribute indicates the version of the data expected by + * the endpoint. The endpoint version is based on the version of the Pub/Sub API. Possible + * values for this attribute are: + *

      + *
    • {@code v1beta1}: uses the push format defined in the v1beta1 Pub/Sub API + *
    • {@code v1} or {@code v1beta2}: uses the push format defined in the v1 Pub/Sub API + *
    + * + *

    If the {@code x-goog-version} attribute is not present when a subscription is created (see + * {@link PubSub#create(SubscriptionInfo)} and {@link PubSub#createAsync(SubscriptionInfo)}), it + * will default to {@code v1}. If it is not present when modifying the push config (see + * {@link PubSub#replacePushConfig(String, PushConfig)} and + * {@link PubSub#replacePushConfigAsync(String, PushConfig)}), its value will not be changed. + * + * @see Message Format + */ + @Deprecated + public Map attributes() { + return getAttributes(); + } + + /** + * Returns the API-supported attributes that can be used to control different aspects of the + * message delivery. + * + *

    The currently supported attribute is {@code x-goog-version}, which can be used to change + * the format of the push message. This attribute indicates the version of the data expected by + * the endpoint. The endpoint version is based on the version of the Pub/Sub API. Possible + * values for this attribute are: + *

      + *
    • {@code v1beta1}: uses the push format defined in the v1beta1 Pub/Sub API + *
    • {@code v1} or {@code v1beta2}: uses the push format defined in the v1 Pub/Sub API + *
    + * + *

    If the {@code x-goog-version} attribute is not present when a subscription is created (see + * {@link PubSub#create(SubscriptionInfo)} and {@link PubSub#createAsync(SubscriptionInfo)}), it + * will default to {@code v1}. If it is not present when modifying the push config (see + * {@link PubSub#replacePushConfig(String, PushConfig)} and + * {@link PubSub#replacePushConfigAsync(String, PushConfig)}), its value will not be changed. + * + * @see Message Format + */ + public Map getAttributes() { + return attributes; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof PushConfig)) { + return false; + } + PushConfig other = (PushConfig) obj; + return Objects.equals(endpoint, other.endpoint) && Objects.equals(attributes, other.attributes); + } + + @Override + public int hashCode() { + return Objects.hash(endpoint, attributes); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("attributes", attributes) + .add("endpoint", endpoint) + .toString(); + } + + /** + * Returns a builder for the {@code PushConfig} object. + */ + public Builder toBuilder() { + return newBuilder(endpoint, attributes); + } + + /** + * Creates a {@code PushConfig} object given the push endpoint. + * + * @param endpoint the URL locating the endpoint to which messages should be pushed. For example, + * an endpoint might use {@code https://example.com/push}. + */ + public static PushConfig of(String endpoint) { + return newBuilder(endpoint).build(); + } + + /** + * Creates a {@code PushConfig} object given the push endpoint and the API-supported attributes + * that can be used to control different aspects of the message delivery. + * + * @param endpoint the URL locating the endpoint to which messages should be pushed. For example, + * an endpoint might use {@code https://example.com/push}. + * @param attributes API supported attributes used to control message delivery. See + * {@link Builder#attributes(Map)} for more details. + */ + public static PushConfig of(String endpoint, Map attributes) { + return newBuilder(endpoint, attributes).build(); + } + + /** + * Creates a builder for {@code PushConfig} objects given the push endpoint. + * + * @param endpoint the URL locating the endpoint to which messages should be pushed. For example, + * an endpoint might use {@code https://example.com/push}. + */ + @Deprecated + public static Builder builder(String endpoint) { + return newBuilder(endpoint); + } + + /** + * Creates a builder for {@code PushConfig} objects given the push endpoint. + * + * @param endpoint the URL locating the endpoint to which messages should be pushed. For example, + * an endpoint might use {@code https://example.com/push}. + */ + public static Builder newBuilder(String endpoint) { + return new Builder().setEndpoint(endpoint); + } + + /** + * Creates a builder for {@code PushConfig} objects given the push endpoint and the API-supported + * attributes that can be used to control different aspects of the message delivery. + * + * @param endpoint the URL locating the endpoint to which messages should be pushed. For example, + * an endpoint might use {@code https://example.com/push}. + * @param attributes API supported attributes used to control message delivery. See + * {@link Builder#attributes(Map)} for more details. + */ + @Deprecated + public static Builder builder(String endpoint, Map attributes) { + return newBuilder(endpoint, attributes); + } + + /** + * Creates a builder for {@code PushConfig} objects given the push endpoint and the API-supported + * attributes that can be used to control different aspects of the message delivery. + * + * @param endpoint the URL locating the endpoint to which messages should be pushed. For example, + * an endpoint might use {@code https://example.com/push}. + * @param attributes API supported attributes used to control message delivery. See + * {@link Builder#attributes(Map)} for more details. + */ + public static Builder newBuilder(String endpoint, Map attributes) { + return newBuilder(endpoint).setAttributes(attributes); + } + + com.google.pubsub.v1.PushConfig toPb() { + return com.google.pubsub.v1.PushConfig.newBuilder().setPushEndpoint(endpoint) + .putAllAttributes(attributes).build(); + } + + static PushConfig fromPb(com.google.pubsub.v1.PushConfig pushConfigPb) { + return newBuilder(pushConfigPb.getPushEndpoint(), pushConfigPb.getAttributesMap()).build(); + } +} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java new file mode 100644 index 000000000000..4afbef2e5a9b --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java @@ -0,0 +1,297 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.cloud.ByteArray; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * A Google Cloud Pub/Sub received message. A received message has all the information in + * {@link Message} as well as the acknowledge id. The ack id can be used to acknowledge the received + * message. + * + *

    {@code ReceivedMessage} also adds a layer of service-related functionality over + * {@link Message} that help manage received messages (see {@link #ack()}, {@link #nack()} and + * {@link #modifyAckDeadline(int, TimeUnit)}). + */ +public final class ReceivedMessage extends Message { + + private static final long serialVersionUID = -4178477763916251733L; + + private final String subscription; + private final String ackId; + private transient PubSub pubsub; + private final PubSubOptions options; + + public static final class Builder extends Message.Builder { + + private final String subscription; + private final String ackId; + private final PubSub pubsub; + private final BuilderImpl delegate; + + private Builder(String subscription, String ackId, PubSub pubsub, BuilderImpl delegate) { + this.subscription = subscription; + this.ackId = ackId; + this.pubsub = pubsub; + this.delegate = delegate; + } + + @Override + Builder setId(String id) { + delegate.setId(id); + return this; + } + + @Override + @Deprecated + public Builder payload(String payload) { + return setPayload(payload); + } + + @Override + public Builder setPayload(String payload) { + delegate.setPayload(payload); + return this; + } + + @Override + @Deprecated + public Builder payload(ByteArray payload) { + return setPayload(payload); + } + + @Override + public Builder setPayload(ByteArray payload) { + delegate.setPayload(payload); + return this; + } + + @Override + @Deprecated + public Builder attributes(Map attributes) { + return setAttributes(attributes); + } + + @Override + public Builder setAttributes(Map attributes) { + delegate.setAttributes(attributes); + return this; + } + + @Override + public Builder addAttribute(String name, String value) { + delegate.addAttribute(name, value); + return this; + } + + @Override + public Builder removeAttribute(String name) { + delegate.removeAttribute(name); + return this; + } + + @Override + public Builder clearAttributes() { + delegate.clearAttributes(); + return this; + } + + @Override + Builder setPublishTime(long publishTime) { + delegate.setPublishTime(publishTime); + return this; + } + + @Override + public ReceivedMessage build() { + return new ReceivedMessage(this); + } + } + + ReceivedMessage(Builder builder) { + super(builder.delegate); + subscription = checkNotNull(builder.subscription); + ackId = checkNotNull(builder.ackId); + pubsub = checkNotNull(builder.pubsub); + options = pubsub.getOptions(); + } + + @Override + public Builder toBuilder() { + return new Builder(subscription, ackId, pubsub, new BuilderImpl(this)); + } + + @Override + public int hashCode() { + return Objects.hash(options, super.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(ReceivedMessage.class)) { + return false; + } + ReceivedMessage other = (ReceivedMessage) obj; + return baseEquals(other) && Objects.equals(options, other.options); + } + + /** + * Returns the received message's {@code PubSub} object used to issue requests. + */ + @Deprecated + public PubSub pubsub() { + return getPubsub(); + } + + /** + * Returns the received message's {@code PubSub} object used to issue requests. + */ + public PubSub getPubsub() { + return pubsub; + } + + /** + * Returns the name of the subscription this message was received from. + */ + @Deprecated + public String subscription() { + return getSubscription(); + } + + /** + * Returns the name of the subscription this message was received from. + */ + public String getSubscription() { + return subscription; + } + + /** + * Returns the acknowledge id of the message. The ack id can be used to acknowledge the received + * message. + */ + @Deprecated + public String ackId() { + return getAckId(); + } + + /** + * Returns the acknowledge id of the message. The ack id can be used to acknowledge the received + * message. + */ + public String getAckId() { + return ackId; + } + + /** + * Acknowledges the current message. + * + * @throws PubSubException upon failure, or if the subscription was not found + */ + public void ack() { + pubsub.ack(subscription, ackId); + } + + /** + * Sends a request to acknowledge the current message. The method returns a {@code Future} object + * that can be used to wait for the acknowledge operation to be completed. + * + * @throws PubSubException upon failure, or if the subscription was not found + */ + public Future ackAsync() { + return pubsub.ackAsync(subscription, ackId); + } + + /** + * "Nacks" the current message. This method corresponds to calling + * {@link #modifyAckDeadline(int, TimeUnit)} with a deadline of 0. + * + * @throws PubSubException upon failure, or if the subscription was not found + */ + public void nack() { + pubsub.nack(subscription, ackId); + } + + /** + * Sends a request to "nack" the current message. This method corresponds to calling + * {@link #modifyAckDeadlineAsync(int, TimeUnit)} with a deadline of 0. The method returns a + * {@code Future} object that can be used to wait for the "nack" operation to be completed. + * + * @throws PubSubException upon failure, or if the subscription was not found + */ + public Future nackAsync() { + return pubsub.nackAsync(subscription, ackId); + } + + /** + * Modifies the acknowledge deadline of the current message. {@code deadline} must be >= 0 and + * is the new deadline with respect to the time the modify request was received by the Pub/Sub + * service. For example, if {@code deadline} is 10 and {@code unit} is {@link TimeUnit#SECONDS}, + * the new ack deadline will expire 10 seconds after the modify request was received by the + * service. Specifying 0 may be used to make the message available for another pull request + * (corresponds to calling {@link #nack()}. + * + * @param deadline the new deadline, relative to the time the modify request is received by the + * Pub/Sub service + * @param unit {@code deadline} time unit + * @throws PubSubException upon failure, or if the subscription was not found + */ + public void modifyAckDeadline(int deadline, TimeUnit unit) { + pubsub.modifyAckDeadline(subscription, deadline, unit, ackId); + } + + /** + * Sends a request to modify the acknowledge deadline of the given messages. {@code deadline} + * must be >= 0 and is the new deadline with respect to the time the modify request was + * received by the Pub/Sub service. For example, if {@code deadline} is 10 and {@code unit} is + * {@link TimeUnit#SECONDS}, the new ack deadline will expire 10 seconds after the modify request + * was received by the service. Specifying 0 may be used to make the message available for another + * pull request (corresponds to calling {@link #nackAsync()}. The method returns a {@code Future} + * object that can be used to wait for the modify operation to be completed. + * + * @param deadline the new deadline, relative to the time the modify request is received by the + * Pub/Sub service + * @param unit {@code deadline} time unit + * @throws PubSubException upon failure, or if the subscription was not found + */ + public Future modifyAckDeadlineAsync(int deadline, TimeUnit unit) { + return pubsub.modifyAckDeadlineAsync(subscription, deadline, unit, ackId); + } + + private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { + input.defaultReadObject(); + this.pubsub = options.getService(); + } + + static ReceivedMessage fromPb(PubSub pubsub, String subscription, + com.google.pubsub.v1.ReceivedMessage msgPb) { + Message message = fromPb(msgPb.getMessage()); + String ackId = msgPb.getAckId(); + return new Builder(subscription, ackId, pubsub, new BuilderImpl(message)).build(); + } +} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java new file mode 100644 index 000000000000..85a270a5fcc0 --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java @@ -0,0 +1,611 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.cloud.GrpcServiceOptions; +import com.google.cloud.Policy; +import com.google.cloud.pubsub.PubSub.MessageConsumer; +import com.google.cloud.pubsub.PubSub.MessageProcessor; +import com.google.cloud.pubsub.PubSub.PullOption; +import com.google.common.base.Function; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Future; + +/** + * A Google Cloud Pub/Sub subscription. A subscription represents the stream of messages from a + * single, specific topic, to be delivered to the subscribing application. Pub/Sub subscriptions + * support both push and pull message delivery. + * + *

    In a push subscription, the Pub/Sub server sends a request to the subscriber application, at a + * preconfigured endpoint (see {@link PushConfig}). The subscriber's HTTP response serves as an + * implicit acknowledgement: a success response indicates that the message has been succesfully + * processed and the Pub/Sub system can delete it from the subscription; a non-success response + * indicates that the Pub/Sub server should resend it (implicit "nack"). + * + *

    In a pull subscription, the subscribing application must explicitly pull messages using one of + * {@link PubSub#pull(String, int)}, {@link PubSub#pullAsync(String, int)} or + * {@link PubSub#pullAsync(String, PubSub.MessageProcessor callback, PubSub.PullOption...)}. + * When messages are pulled with {@link PubSub#pull(String, int)} or + * {@link PubSub#pullAsync(String, int)} the subscribing application must also explicitly + * acknowledge them using one of {@link PubSub#ack(String, Iterable)}, + * {@link PubSub#ack(String, String, String...)}, {@link PubSub#ackAsync(String, Iterable)} or + * {@link PubSub#ackAsync(String, String, String...)}. + * + *

    {@code Subscription} adds a layer of service-related functionality over + * {@link SubscriptionInfo}. Objects of this class are immutable. To get a {@code Subscription} + * object with the most recent information use {@link #reload} or {@link #reloadAsync}. + * + * @see Pub/Sub Data Model + * @see Subscriber Guide + */ +public class Subscription extends SubscriptionInfo { + + private static final long serialVersionUID = -4153366055659552230L; + + private final PubSubOptions options; + private transient PubSub pubsub; + + /** + * A builder for {@code Subscription} objects. + */ + public static final class Builder extends SubscriptionInfo.Builder { + + private final PubSub pubsub; + private final BuilderImpl delegate; + + private Builder(Subscription subscription) { + pubsub = subscription.pubsub; + delegate = new BuilderImpl(subscription); + } + + @Override + @Deprecated + public Builder topic(TopicId topic) { + return setTopic(topic); + } + + @Override + public Builder setTopic(TopicId topic) { + delegate.setTopic(topic); + return this; + } + + @Override + @Deprecated + public Builder topic(String project, String topic) { + return setTopic(project, topic); + } + + @Override + public Builder setTopic(String project, String topic) { + delegate.setTopic(project, topic); + return this; + } + + @Override + @Deprecated + public Builder topic(String topic) { + return setTopic(topic); + } + + @Override + public Builder setTopic(String topic) { + delegate.setTopic(topic); + return this; + } + + @Override + @Deprecated + public Builder name(String name) { + return setName(name); + } + + @Override + public Builder setName(String name) { + delegate.setName(name); + return this; + } + + @Override + @Deprecated + public Builder pushConfig(PushConfig pushConfig) { + return setPushConfig(pushConfig); + } + + @Override + public Builder setPushConfig(PushConfig pushConfig) { + delegate.setPushConfig(pushConfig); + return this; + } + + @Override + @Deprecated + public Builder ackDeadLineSeconds(int ackDeadLineSeconds) { + return setAckDeadLineSeconds(ackDeadLineSeconds); + } + + @Override + public Builder setAckDeadLineSeconds(int ackDeadLineSeconds) { + delegate.setAckDeadLineSeconds(ackDeadLineSeconds); + return this; + } + + @Override + public Subscription build() { + return new Subscription(this.pubsub, this.delegate); + } + } + + Subscription(PubSub pubsub, BuilderImpl builder) { + super(builder); + this.pubsub = checkNotNull(pubsub); + options = pubsub.getOptions(); + } + + @Override + public Builder toBuilder() { + return new Builder(this); + } + + @Override + public final int hashCode() { + return Objects.hash(options, super.hashCode()); + } + + @Override + public final boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || !obj.getClass().equals(Subscription.class)) { + return false; + } + Subscription other = (Subscription) obj; + return baseEquals(other) && Objects.equals(options, other.options); + } + + /** + * Returns the subscription's {@code PubSub} object used to issue requests. + */ + @Deprecated + public PubSub pubSub() { + return getPubsub(); + } + + /** + * Returns the subscription's {@code PubSub} object used to issue requests. + */ + public PubSub getPubsub() { + return pubsub; + } + + /** + * Deletes this subscription. + * + *

    Example of deleting the subscription. + *

     {@code
    +   * boolean deleted = subscription.delete();
    +   * if (deleted) {
    +   *   // the subscription was deleted
    +   * } else {
    +   *   // the subscription was not found
    +   * }
    +   * }
    + * + * @return {@code true} if the subscription was deleted, {@code false} if it was not found + * @throws PubSubException upon failure + */ + public boolean delete() { + return pubsub.deleteSubscription(getName()); + } + + /** + * Sends a request for deleting this subscription. This method returns a {@code Future} object to + * consume the result. {@link Future#get()} returns {@code true} if the subscription was deleted, + * {@code false} if it was not found. + * + *

    Example of asynchronously deleting the subscription. + *

     {@code
    +   * Future future = subscription.deleteAsync();
    +   * // ...
    +   * boolean deleted = future.get();
    +   * if (deleted) {
    +   *   // the subscription was deleted
    +   * } else {
    +   *   // the subscription was not found
    +   * }
    +   * }
    + * + */ + public Future deleteAsync() { + return pubsub.deleteSubscriptionAsync(getName()); + } + + /** + * Fetches current subscription's latest information. Returns {@code null} if the subscription + * does not exist. + * + *

    Example of getting the subscription's latest information. + *

     {@code
    +   * Subscription latestSubscription = subscription.reload();
    +   * if (latestSubscription == null) {
    +   *   // the subscription was not found
    +   * }
    +   * }
    + * + * @return a {@code Subscription} object with latest information or {@code null} if not found + * @throws PubSubException upon failure + */ + public Subscription reload() { + return pubsub.getSubscription(getName()); + } + + /** + * Sends a request for fetching current subscription's latest information. This method returns a + * {@code Future} object to consume the result. {@link Future#get()} returns the requested + * subscription or {@code null} if not found. + * + *

    Example of asynchronously getting the subscription's latest information. + *

     {@code
    +   * Future future = subscription.reloadAsync();
    +   * // ...
    +   * Subscription latestSubscription = future.get();
    +   * if (latestSubscription == null) {
    +   *   // the subscription was not found
    +   * }
    +   * }
    + * + * @return a {@code Subscription} object with latest information or {@code null} if not found + * @throws PubSubException upon failure + */ + public Future reloadAsync() { + return pubsub.getSubscriptionAsync(getName()); + } + + /** + * Sets the push configuration for this subscription. This may be used to change a push + * subscription to a pull one (passing a {@code null} {@code pushConfig} parameter) or vice versa. + * This methods can also be used to change the endpoint URL and other attributes of a push + * subscription. Messages will accumulate for delivery regardless of changes to the push + * configuration. + * + *

    Example of replacing the push configuration of the subscription, setting the push endpoint. + *

     {@code
    +   * String endpoint = "https://www.example.com/push";
    +   * PushConfig pushConfig = PushConfig.of(endpoint);
    +   * subscription.replacePushConfig(pushConfig);
    +   * }
    + * + *

    Example of replacing the push configuration of the subscription, making it a pull + * subscription. + *

     {@code
    +   * subscription.replacePushConfig(null);
    +   * }
    + * + * @param pushConfig the new push configuration. Use {@code null} to unset it + * @throws PubSubException upon failure, or if the subscription does not exist + */ + public void replacePushConfig(PushConfig pushConfig) { + pubsub.replacePushConfig(getName(), pushConfig); + } + + /** + * Sends a request for updating the push configuration for a specified subscription. This may be + * used to change a push subscription to a pull one (passing a {@code null} {@code pushConfig} + * parameter) or vice versa. This methods can also be used to change the endpoint URL and other + * attributes of a push subscription. Messages will accumulate for delivery regardless of changes + * to the push configuration. The method returns a {@code Future} object that can be used to wait + * for the replace operation to be completed. + * + *

    Example of asynchronously replacing the push configuration of the subscription, setting the + * push endpoint. + *

     {@code
    +   * String endpoint = "https://www.example.com/push";
    +   * PushConfig pushConfig = PushConfig.of(endpoint);
    +   * Future future = subscription.replacePushConfigAsync(pushConfig);
    +   * // ...
    +   * future.get();
    +   * }
    + * + *

    Example of asynchronously replacing the push configuration of the subscription, making it a + * pull subscription. + *

     {@code
    +   * Future future = subscription.replacePushConfigAsync(null);
    +   * // ...
    +   * future.get();
    +   * }
    + * + * @param pushConfig the new push configuration. Use {@code null} to unset it + * @return a {@code Future} to wait for the replace operation to be completed. + */ + public Future replacePushConfigAsync(PushConfig pushConfig) { + return pubsub.replacePushConfigAsync(getName(), pushConfig); + } + + /** + * Pulls messages from this subscription. This method possibly returns no messages if no message + * was available at the time the request was processed by the Pub/Sub service (i.e. the system is + * not allowed to wait until at least one message is available). Pulled messages have their + * acknowledge deadline automatically renewed until they are explicitly consumed using + * {@link Iterator#next()}. + * + *

    Example of pulling a maximum number of messages from the subscription. + *

     {@code
    +   * Iterator messages = subscription.pull(100);
    +   * // Ack deadline is renewed until the message is consumed
    +   * while (messages.hasNext()) {
    +   *   ReceivedMessage message = messages.next();
    +   *   // do something with message and ack/nack it
    +   *   message.ack(); // or message.nack()
    +   * }
    +   * }
    + * + * @param maxMessages the maximum number of messages pulled by this method. This method can + * possibly return fewer messages. + * @throws PubSubException upon failure + */ + public Iterator pull(int maxMessages) { + return pubsub.pull(getName(), maxMessages); + } + + /** + * Sends a request for pulling messages from this subscription. This method returns a + * {@code Future} object to consume the result. {@link Future#get()} returns a message iterator. + * This method possibly returns no messages if no message was available at the time the request + * was processed by the Pub/Sub service (i.e. the system is not allowed to wait until at least one + * message is available). + * + *

    Example of asynchronously pulling a maximum number of messages from the subscription. + *

     {@code
    +   * Future> future = subscription.pullAsync(100);
    +   * // ...
    +   * Iterator messages = future.get();
    +   * // Ack deadline is renewed until the message is consumed
    +   * while (messages.hasNext()) {
    +   *   ReceivedMessage message = messages.next();
    +   *   // do something with message and ack/nack it
    +   *   message.ack(); // or message.nack()
    +   * }
    +   * }
    + * + * @param maxMessages the maximum number of messages pulled by this method. This method can + * possibly return fewer messages. + * @throws PubSubException upon failure + */ + public Future> pullAsync(int maxMessages) { + return pubsub.pullAsync(getName(), maxMessages); + } + + /** + * Creates a message consumer that pulls messages from this subscription. You can stop pulling + * messages by calling {@link MessageConsumer#close()}. The returned message consumer executes + * {@link MessageProcessor#process(Message)} on each pulled message. If + * {@link MessageProcessor#process(Message)} executes correctly, the message is acknowledged. If + * {@link MessageProcessor#process(Message)} throws an exception, the message is "nacked". For + * all pulled messages, the ack deadline is automatically renewed until the message is either + * acknowledged or "nacked". + * + *

    The {@link PullOption#maxQueuedCallbacks(int)} option can be used to control the maximum + * number of queued messages (messages either being processed or waiting to be processed). The + * {@link PullOption#executorFactory(GrpcServiceOptions.ExecutorFactory)} can be used to provide + * an executor to run message processor callbacks. + * + *

    Example of continuously pulling messages from the subscription. + *

     {@code
    +   * String subscriptionName = "my_subscription_name";
    +   * MessageProcessor callback = new MessageProcessor() {
    +   *   public void process(Message message) throws Exception {
    +   *     // Ack deadline is renewed until this method returns
    +   *     // Message is acked if this method returns successfully
    +   *     // Message is nacked if this method throws an exception
    +   *   }
    +   * };
    +   * MessageConsumer consumer = subscription.pullAsync(callback);
    +   * // ...
    +   * // Stop pulling
    +   * consumer.close();
    +   * }
    + * + * @param callback the callback to be executed on each message + * @param options pulling options + * @return a message consumer for the provided subscription and options + */ + public MessageConsumer pullAsync(MessageProcessor callback, PullOption... options) { + return pubsub.pullAsync(getName(), callback, options); + } + + /** + * Returns the IAM access control policy for this subscription. Returns {@code null} if the + * subscription was not found. + * + *

    Example of getting the subscription's policy. + *

     {@code
    +   * Policy policy = subscription.getPolicy();
    +   * if (policy == null) {
    +   *   // subscription was not found
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + public Policy getPolicy() { + return pubsub.getSubscriptionPolicy(this.getName()); + } + + /** + * Sends a request for getting the IAM access control policy for this subscription. This method + * returns a {@code Future} object to consume the result. {@link Future#get()} returns the + * requested policy or {@code null} if the subscription was not found. + * + *

    Example of asynchronously getting the subscription's policy. + *

     {@code
    +   * Future future = subscription.getPolicyAsync();
    +   * // ...
    +   * Policy policy = future.get();
    +   * if (policy == null) {
    +   *   // subscription was not found
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + public Future getPolicyAsync() { + return pubsub.getSubscriptionPolicyAsync(this.getName()); + } + + /** + * Sets the IAM access control policy for this subscription. Replaces any existing policy. This + * method returns the new policy. + * + *

    It is recommended that you use the read-modify-write pattern. This pattern entails reading + * the project's current policy, updating it locally, and then sending the modified policy for + * writing. Cloud IAM solves the problem of conflicting processes simultaneously attempting to + * modify a policy by using the {@link Policy#etag etag} property. This property is used to + * verify whether the policy has changed since the last request. When you make a request with an + * etag value, the value in the request is compared with the existing etag value associated with + * the policy. The policy is written only if the etag values match. If the etags don't match, a + * {@code PubSubException} is thrown, denoting that the server aborted update. If an etag is not + * provided, the policy is overwritten blindly. + * + *

    Example of replacing the subscription's policy. + *

     {@code
    +   * Policy policy = subscription.getPolicy();
    +   * Policy updatedPolicy = policy.toBuilder()
    +   *     .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers())
    +   *     .build();
    +   * updatedPolicy = subscription.replacePolicy(updatedPolicy);
    +   * }
    + * + * @throws PubSubException upon failure + */ + public Policy replacePolicy(Policy newPolicy) { + return pubsub.replaceSubscriptionPolicy(this.getName(), newPolicy); + } + + /** + * Sends a request to set the IAM access control policy for this subscription. Replaces any + * existing policy. This method returns a {@code Future} object to consume the result. + * {@link Future#get()} returns the new policy. + * + *

    It is recommended that you use the read-modify-write pattern. This pattern entails reading + * the project's current policy, updating it locally, and then sending the modified policy for + * writing. Cloud IAM solves the problem of conflicting processes simultaneously attempting to + * modify a policy by using the {@link Policy#etag etag} property. This property is used to + * verify whether the policy has changed since the last request. When you make a request with an + * etag value, the value in the request is compared with the existing etag value associated with + * the policy. The policy is written only if the etag values match. If the etags don't match, + * {@link Future#get()} will throw a {@link java.util.concurrent.ExecutionException} caused by a + * {@code PubSubException}, denoting that the server aborted update. If an etag is not provided, + * the policy is overwritten blindly. + * + *

    Example of asynchronously replacing the subscription's policy. + *

     {@code
    +   * Policy policy = subscription.getPolicy();
    +   * Policy updatedPolicy = policy.toBuilder()
    +   *     .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers())
    +   *     .build();
    +   * Future future = subscription.replacePolicyAsync(updatedPolicy);
    +   * // ...
    +   * updatedPolicy = future.get();
    +   * }
    + * + * @throws PubSubException upon failure + */ + public Future replacePolicyAsync(Policy newPolicy) { + return pubsub.replaceSubscriptionPolicyAsync(this.getName(), newPolicy); + } + + /** + * Returns the permissions that a caller has on this subscription. You typically don't call this + * method if you're using Google Cloud Platform directly to manage permissions. This method is + * intended for integration with your proprietary software, such as a customized graphical user + * interface. For example, the Cloud Platform Console tests IAM permissions internally to + * determine which UI should be available to the logged-in user. + * + *

    Example of testing whether the caller has the provided permissions on the subscription. + *

     {@code
    +   * List permissions = new LinkedList<>();
    +   * permissions.add("pubsub.subscriptions.get");
    +   * List testedPermissions = subscription.testPermissions(permissions);
    +   * }
    + * + * @return A list of booleans representing whether the caller has the permissions specified (in + * the order of the given permissions) + * @throws PubSubException upon failure + * @see + * Permissions and Roles + */ + public List testPermissions(List permissions) { + return pubsub.testSubscriptionPermissions(this.getName(), permissions); + } + + /** + * Sends a request to get the permissions that a caller has on this subscription. + * + *

    You typically don't call this method if you're using Google Cloud Platform directly to + * manage permissions. This method is intended for integration with your proprietary software, + * such as a customized graphical user interface. For example, the Cloud Platform Console tests + * IAM permissions internally to determine which UI should be available to the logged-in user. + * + *

    Example of asynchronously testing whether the caller has the provided permissions on the + * subscription. + *

     {@code
    +   * List permissions = new LinkedList<>();
    +   * permissions.add("pubsub.subscriptions.get");
    +   * Future> future = subscription.testPermissionsAsync(permissions);
    +   * // ...
    +   * List testedPermissions = future.get();
    +   * }
    + * + * @return A {@code Future} object to consume the result. {@link Future#get()} returns a list of + * booleans representing whether the caller has the permissions specified (in the order of the + * given permissions) + * @throws PubSubException upon failure + * @see + * Permissions and Roles + */ + public Future> testPermissionsAsync(List permissions) { + return pubsub.testSubscriptionPermissionsAsync(this.getName(), permissions); + } + + private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { + input.defaultReadObject(); + this.pubsub = options.getService(); + } + + static Subscription fromPb(PubSub storage, com.google.pubsub.v1.Subscription subscriptionPb) { + SubscriptionInfo subscriptionInfo = SubscriptionInfo.fromPb(subscriptionPb); + return new Subscription(storage, new BuilderImpl(subscriptionInfo)); + } + + static Function fromPbFunction( + final PubSub pubsub) { + return new Function() { + @Override + public Subscription apply(com.google.pubsub.v1.Subscription subscriptionPb) { + return subscriptionPb != null ? fromPb(pubsub, subscriptionPb) : null; + } + }; + } +} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Topic.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Topic.java new file mode 100644 index 000000000000..3a5a8dd7c58c --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/Topic.java @@ -0,0 +1,556 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.cloud.AsyncPage; +import com.google.cloud.Page; +import com.google.cloud.Policy; +import com.google.cloud.pubsub.PubSub.ListOption; +import com.google.common.base.Function; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Future; + +/** + * A Google Cloud Pub/Sub topic. A topic is a named resource to which messages are sent by + * publishers. Subscribers can receive messages sent to a topic by creating subscriptions. + * {@code Topic} adds a layer of service-related functionality over {@link TopicInfo}. Objects of + * this class are immutable. To get a {@code Topic} object with the most recent information use + * {@link #reload} or {@link #reloadAsync}. + * + * @see Pub/Sub Data Model + */ +public class Topic extends TopicInfo { + + private static final long serialVersionUID = -2686692223763315944L; + + private final PubSubOptions options; + private transient PubSub pubsub; + + /** + * A builder for {@code Topic} objects. + */ + public static final class Builder extends TopicInfo.Builder { + + private final PubSub pubsub; + private final BuilderImpl delegate; + + private Builder(Topic topic) { + pubsub = topic.pubsub; + delegate = new BuilderImpl(topic); + } + + @Override + @Deprecated + public Builder name(String name) { + return setName(name); + } + + @Override + public Builder setName(String name) { + delegate.setName(name); + return this; + } + + @Override + public Topic build() { + return new Topic(this.pubsub, this.delegate); + } + } + + Topic(PubSub pubsub, BuilderImpl builder) { + super(builder); + this.pubsub = checkNotNull(pubsub); + options = pubsub.getOptions(); + } + + @Override + public Builder toBuilder() { + return new Builder(this); + } + + @Override + public final int hashCode() { + return Objects.hash(options, super.hashCode()); + } + + @Override + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(Topic.class)) { + return false; + } + Topic other = (Topic) obj; + return baseEquals(other) && Objects.equals(options, other.options); + } + + /** + * Returns the topic's {@code PubSub} object used to issue requests. + */ + @Deprecated + public PubSub pubSub() { + return getPubsub(); + } + + /** + * Returns the topic's {@code PubSub} object used to issue requests. + */ + public PubSub getPubsub() { + return pubsub; + } + + /** + * Deletes this topic. + * + *

    Example of deleting the topic. + *

     {@code
    +   * boolean deleted = topic.delete();
    +   * if (deleted) {
    +   *   // the topic was deleted
    +   * } else {
    +   *   // the topic was not found
    +   * }
    +   * }
    + * + * @return {@code true} if the topic was deleted, {@code false} if it was not found + * @throws PubSubException upon failure + */ + public boolean delete() { + return pubsub.deleteTopic(getName()); + } + + /** + * Sends a request for deleting this topic. This method returns a {@code Future} object to consume + * the result. {@link Future#get()} returns {@code true} if the topic was deleted, {@code false} + * if it was not found. + * + *

    Example of asynchronously deleting the topic. + *

     {@code
    +   * Future future = topic.deleteAsync();
    +   * // ...
    +   * boolean deleted = future.get();
    +   * if (deleted) {
    +   *   // the topic was deleted
    +   * } else {
    +   *   // the topic was not found
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + public Future deleteAsync() { + return pubsub.deleteTopicAsync(getName()); + } + + /** + * Fetches current topic's latest information. Returns {@code null} if the topic does not exist. + * + *

    Example of getting the topic's latest information. + *

     {@code
    +   * Topic latestTopic = topic.reload();
    +   * if (latestTopic == null) {
    +   *   // the topic was not found
    +   * }
    +   * }
    + * + * @return a {@code Topic} object with latest information or {@code null} if not found + * @throws PubSubException upon failure + */ + public Topic reload() { + return pubsub.getTopic(getName()); + } + + /** + * Sends a request to fetch current topic's latest information. This method returns a + * {@code Future} object to consume the result. {@link Future#get()} returns a {@code Topic} + * object with latest information or {@code null} if not found. + * + *

    Example of asynchronously getting the topic's latest information. + *

     {@code
    +   * Future future = topic.reloadAsync();
    +   * // ...
    +   * Topic latestTopic = future.get();
    +   * if (latestTopic == null) {
    +   *   // the topic was not found
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + public Future reloadAsync() { + return pubsub.getTopicAsync(getName()); + } + + /** + * Publishes a message to this topic. This method returns a service-generated id for the published + * message. Service-generated ids are guaranteed to be unique within the topic. + * + *

    Example of publishing one message to the topic. + *

     {@code
    +   * Message message = Message.of("payload");
    +   * String messageId = topic.publish(message);
    +   * }
    + * + * @param message the message to publish + * @return a unique service-generated id for the message + * @throws PubSubException upon failure, if the topic does not exist or if the message has empty + * payload and no attributes + */ + public String publish(Message message) { + return pubsub.publish(getName(), message); + } + + /** + * Sends a request for publishing a message to the this topic. This method returns a + * {@code Future} object to consume the result. {@link Future#get()} returns a service-generated + * id for the published message. Service-generated ids are guaranteed to be unique within the + * topic. + * + *

    Example of asynchronously publishing one message to the topic. + *

     {@code
    +   * Message message = Message.of("payload");
    +   * Future future = topic.publishAsync(message);
    +   * // ...
    +   * String messageId = future.get();
    +   * }
    + * + * @param message the message to publish + * @return a {@code Future} for the unique service-generated id for the message + */ + public Future publishAsync(Message message) { + return pubsub.publishAsync(getName(), message); + } + + /** + * Publishes a number of messages to this topic. This method returns a list of service-generated + * ids for the published messages. Service-generated ids are guaranteed to be unique within the + * topic. + * + *

    Example of publishing some messages to the topic. + *

     {@code
    +   * Message message1 = Message.of("payload1");
    +   * Message message2 = Message.of("payload2");
    +   * List messageIds = topic.publish(message1, message2);
    +   * }
    + * + * @param message the first message to publish + * @param messages other messages to publish + * @return a list of unique, service-generated, ids. Ids are in the same order as the messages. + * @throws PubSubException upon failure, if the topic does not exist or if one of the messages has + * empty payload and no attributes + */ + public List publish(Message message, Message... messages) { + return pubsub.publish(getName(), message, messages); + } + + /** + * Sends a request to publish a number of messages to this topic. This method returns a + * {@code Future} object to consume the result. {@link Future#get()} returns a list of + * service-generated ids for the published messages. Service-generated ids are guaranteed to be + * unique within the topic. + * + *

    Example of asynchronously publishing some messages to the topic. + *

     {@code
    +   * Message message1 = Message.of("payload1");
    +   * Message message2 = Message.of("payload2");
    +   * Future> future = topic.publishAsync(message1, message2);
    +   * // ...
    +   * List messageIds = future.get();
    +   * }
    + * + * @param message the first message to publish + * @param messages other messages to publish + * @return a {@code Future} for the unique, service-generated ids. Ids are in the same order as + * the messages. + */ + public Future> publishAsync(Message message, Message... messages) { + return pubsub.publishAsync(getName(), message, messages); + } + + /** + * Publishes a number of messages to this topic. This method returns a list ofservice-generated + * ids for the published messages. Service-generated ids are guaranteed to be unique within the + * topic. + * + *

    Example of publishing a list of messages to the topic. + *

     {@code
    +   * List messages = new LinkedList<>();
    +   * messages.add(Message.of("payload1"));
    +   * messages.add(Message.of("payload2"));
    +   * List messageIds = topic.publish(messages);
    +   * }
    + * + * @param messages the messages to publish + * @return a list of unique, service-generated, ids. Ids are in the same order as the messages. + * @throws PubSubException upon failure, if the topic does not exist or if one of the messages has + * empty payload and no attributes + */ + public List publish(Iterable messages) { + return pubsub.publish(getName(), messages); + } + + /** + * Sends a request to publish a number of messages to this topic. This method returns a + * {@code Future} object to consume the result. {@link Future#get()} returns a list of + * service-generated ids for the published messages. Service-generated ids are guaranteed to be + * unique within the topic. + * + *

    Example of asynchronously publishing a list of messages to the topic. + *

     {@code
    +   * List messages = new LinkedList<>();
    +   * messages.add(Message.of("payload1"));
    +   * messages.add(Message.of("payload2"));
    +   * Future> future = topic.publishAsync(messages);
    +   * // ...
    +   * List messageIds = future.get();
    +   * }
    + * + * @param messages the messages to publish + * @return a {@code Future} for the unique, service-generated ids. Ids are in the same order as + * the messages. + */ + public Future> publishAsync(Iterable messages) { + return pubsub.publishAsync(getName(), messages); + } + + /** + * Lists the identities of the subscriptions for this topic. This method returns a {@link Page} + * object that can be used to consume paginated results. Use {@link ListOption} to specify the + * page size or the page token from which to start listing subscriptions. + * + *

    Example of listing subscriptions for the topic, specifying the page size. + *

     {@code
    +   * Page subscriptions = topic.listSubscriptions(ListOption.pageSize(100));
    +   * Iterator subscriptionIterator = subscriptions.iterateAll();
    +   * while (subscriptionIterator.hasNext()) {
    +   *   SubscriptionId subscription = subscriptionIterator.next();
    +   *   // do something with the subscription identity
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + public Page listSubscriptions(ListOption... options) { + return pubsub.listSubscriptions(getName(), options); + } + + /** + * Sends a request for listing the identities of subscriptions for this topic. This method returns + * a {@code Future} object to consume the result. {@link Future#get()} returns an + * {@link AsyncPage} object that can be used to asynchronously handle paginated results. Use + * {@link ListOption} to specify the page size or the page token from which to start listing + * subscriptions. + * + *

    Example of asynchronously listing subscriptions for the topic, specifying the page size. + *

     {@code
    +   * Future> future =
    +   *     topic.listSubscriptionsAsync(ListOption.pageSize(100));
    +   * // ...
    +   * AsyncPage subscriptions = future.get();
    +   * Iterator subscriptionIterator = subscriptions.iterateAll();
    +   * while (subscriptionIterator.hasNext()) {
    +   *   SubscriptionId subscription = subscriptionIterator.next();
    +   *   // do something with the subscription identity
    +   * }
    +   * }
    + * + */ + public Future> listSubscriptionsAsync(ListOption... options) { + return pubsub.listSubscriptionsAsync(getName(), options); + } + + /** + * Returns the IAM access control policy for this topic. Returns {@code null} if the topic was not + * found. + * + *

    Example of getting the topic's policy. + *

     {@code
    +   * Policy policy = topic.getPolicy();
    +   * if (policy == null) {
    +   *   // topic was not found
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + public Policy getPolicy() { + return pubsub.getTopicPolicy(this.getName()); + } + + /** + * Sends a request for getting the IAM access control policy for this topic. This method returns a + * {@code Future} object to consume the result. {@link Future#get()} returns the requested policy + * or {@code null} if the topic was not found. + * + *

    Example of asynchronously getting the topic's policy. + *

     {@code
    +   * Future future = topic.getPolicyAsync();
    +   * // ...
    +   * Policy policy = future.get();
    +   * if (policy == null) {
    +   *   // topic was not found
    +   * }
    +   * }
    + * + * @throws PubSubException upon failure + */ + public Future getPolicyAsync() { + return pubsub.getTopicPolicyAsync(this.getName()); + } + + /** + * Sets the IAM access control policy for this topic. Replaces any existing policy. This method + * returns the new policy. + * + *

    It is recommended that you use the read-modify-write pattern. This pattern entails reading + * the project's current policy, updating it locally, and then sending the modified policy for + * writing. Cloud IAM solves the problem of conflicting processes simultaneously attempting to + * modify a policy by using the {@link Policy#etag etag} property. This property is used to + * verify whether the policy has changed since the last request. When you make a request with an + * etag value, the value in the request is compared with the existing etag value associated with + * the policy. The policy is written only if the etag values match. If the etags don't match, a + * {@code PubSubException} is thrown, denoting that the server aborted update. If an etag is not + * provided, the policy is overwritten blindly. + * + *

    Example of replacing the topic's policy. + *

     {@code
    +   * Policy policy = topic.getPolicy();
    +   * Policy updatedPolicy = policy.toBuilder()
    +   *     .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers())
    +   *     .build();
    +   * updatedPolicy = topic.replacePolicy(updatedPolicy);
    +   * }
    + * + * @throws PubSubException upon failure + */ + public Policy replacePolicy(Policy newPolicy) { + return pubsub.replaceTopicPolicy(this.getName(), newPolicy); + } + + /** + * Sends a request to set the IAM access control policy for this topic. Replaces any existing + * policy. This method returns a {@code Future} object to consume the result. {@link Future#get()} + * returns the new policy. + * + *

    It is recommended that you use the read-modify-write pattern. This pattern entails reading + * the project's current policy, updating it locally, and then sending the modified policy for + * writing. Cloud IAM solves the problem of conflicting processes simultaneously attempting to + * modify a policy by using the {@link Policy#etag etag} property. This property is used to + * verify whether the policy has changed since the last request. When you make a request with an + * etag value, the value in the request is compared with the existing etag value associated with + * the policy. The policy is written only if the etag values match. If the etags don't match, + * {@link Future#get()} will throw a {@link java.util.concurrent.ExecutionException} caused by a + * {@code PubSubException}, denoting that the server aborted update. If an etag is not provided, + * the policy is overwritten blindly. + * + *

    Example of asynchronously replacing the topic's policy. + *

     {@code
    +   * Policy policy = topic.getPolicy();
    +   * Policy updatedPolicy = policy.toBuilder()
    +   *     .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers())
    +   *     .build();
    +   * Future future = topic.replacePolicyAsync(updatedPolicy);
    +   * // ...
    +   * updatedPolicy = future.get();
    +   * }
    + * + * @throws PubSubException upon failure + */ + public Future replacePolicyAsync(Policy newPolicy) { + return pubsub.replaceTopicPolicyAsync(this.getName(), newPolicy); + } + + /** + * Returns the permissions that a caller has on this topic. + * + *

    You typically don't call this method if you're using Google Cloud Platform directly to + * manage permissions. This method is intended for integration with your proprietary software, + * such as a customized graphical user interface. For example, the Cloud Platform Console tests + * IAM permissions internally to determine which UI should be available to the logged-in user. + * + *

    Example of testing whether the caller has the provided permissions on the topic. + *

     {@code
    +   * List permissions = new LinkedList<>();
    +   * permissions.add("pubsub.topics.get");
    +   * List testedPermissions = topic.testPermissions(permissions);
    +   * }
    + * + * @return A list of booleans representing whether the caller has the permissions specified (in + * the order of the given permissions) + * @throws PubSubException upon failure + * @see + * Permissions and Roles + */ + public List testPermissions(List permissions) { + return pubsub.testTopicPermissions(this.getName(), permissions); + } + + /** + * Sends a request to get the permissions that a caller has on this topic. + * + *

    You typically don't call this method if you're using Google Cloud Platform directly to + * manage permissions. This method is intended for integration with your proprietary software, + * such as a customized graphical user interface. For example, the Cloud Platform Console tests + * IAM permissions internally to determine which UI should be available to the logged-in user. + * + *

    Example of asynchronously testing whether the caller has the provided permissions on the + * topic. + *

     {@code
    +   * List permissions = new LinkedList<>();
    +   * permissions.add("pubsub.topics.get");
    +   * Future> future = topic.testPermissionsAsync(permissions);
    +   * // ...
    +   * List testedPermissions = future.get();
    +   * }
    + * + * @return A {@code Future} object to consume the result. {@link Future#get()} returns a list of + * booleans representing whether the caller has the permissions specified (in the order of the + * given permissions) + * @throws PubSubException upon failure + * @see + * Permissions and Roles + */ + public Future> testPermissionsAsync(List permissions) { + return pubsub.testTopicPermissionsAsync(this.getName(), permissions); + } + + private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { + input.defaultReadObject(); + this.pubsub = options.getService(); + } + + static Topic fromPb(PubSub pubsub, com.google.pubsub.v1.Topic topicPb) { + TopicInfo topicInfo = TopicInfo.fromPb(topicPb); + return new Topic(pubsub, new BuilderImpl(topicInfo)); + } + + static Function fromPbFunction(final PubSub pubsub) { + return new Function() { + @Override + public Topic apply(com.google.pubsub.v1.Topic topicPb) { + return topicPb != null ? fromPb(pubsub, topicPb) : null; + } + }; + } +} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/package-info.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/package-info.java new file mode 100644 index 000000000000..c98a5f5c8820 --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/package-info.java @@ -0,0 +1,54 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A client to Google Cloud Pub/Sub. + * + *

    Here's a simple usage example for using google-cloud from Compute Engine/App Engine Flexible. + * This example shows how to create a Pub/Sub topic and asynchronously publish messages to it. For + * the complete source code see + * + * CreateTopicAndPublishMessages.java. + *

     {@code
    + * try (PubSub pubsub = PubSubOptions.getDefaultInstance().getService()) {
    + *   Topic topic = pubsub.create(TopicInfo.of("test-topic"));
    + *   Message message1 = Message.of("First message");
    + *   Message message2 = Message.of("Second message");
    + *   topic.publishAsync(message1, message2);
    + * }}
    + * + *

    This second example shows how to create a Pub/Sub pull subscription and asynchronously pull + * messages from it. For the complete source code see + * + * CreateSubscriptionAndPullMessages.java. + *

     {@code
    + * try (PubSub pubsub = PubSubOptions.getDefaultInstance().getService()) {
    + *   Subscription subscription =
    + *   pubsub.create(SubscriptionInfo.of("test-topic", "test-subscription"));
    + *   MessageProcessor callback = new MessageProcessor() {
    + *     public void process(Message message) throws Exception {
    + *       System.out.printf("Received message \"%s\"%n", message.payloadAsString());
    + *     }
    + *   };
    + *   // Create a message consumer and pull messages (for 60 seconds)
    + *   try (MessageConsumer consumer = subscription.pullAsync(callback)) {
    + *     Thread.sleep(60_000);
    + *   }
    + * }}
    + * + * @see Google Cloud Pub/Sub + */ +package com.google.cloud.pubsub; diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpcFactory.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpcFactory.java new file mode 100644 index 000000000000..d3648a68399f --- /dev/null +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpcFactory.java @@ -0,0 +1,27 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub.spi; + +import com.google.cloud.pubsub.PubSubOptions; +import com.google.cloud.spi.ServiceRpcFactory; + +/** + * An interface for Pub/Sub RPC factory. + * Implementation will be loaded via {@link java.util.ServiceLoader}. + */ +public interface PubSubRpcFactory extends ServiceRpcFactory { +} diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/MessageDispatcher.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/MessageDispatcher.java index c378d3e7e39c..84a073eb3d3e 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/MessageDispatcher.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/MessageDispatcher.java @@ -16,7 +16,7 @@ package com.google.cloud.pubsub.spi.v1; -import com.google.api.gax.bundling.FlowController; +import com.google.api.gax.grpc.FlowController; import com.google.api.stats.Distribution; import com.google.cloud.Clock; import com.google.cloud.pubsub.spi.v1.MessageReceiver.AckReply; diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PollingSubscriberConnection.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PollingSubscriberConnection.java index bd0dbdb02c23..0975e3d6b70d 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PollingSubscriberConnection.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PollingSubscriberConnection.java @@ -18,7 +18,7 @@ import static com.google.cloud.pubsub.spi.v1.StatusUtil.isRetryable; -import com.google.api.gax.bundling.FlowController; +import com.google.api.gax.grpc.FlowController; import com.google.api.stats.Distribution; import com.google.auth.Credentials; import com.google.cloud.Clock; diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/Publisher.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/Publisher.java index 722d26f17051..6a476f695431 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/Publisher.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/Publisher.java @@ -16,7 +16,8 @@ package com.google.cloud.pubsub.spi.v1; -import com.google.api.gax.bundling.FlowController; +import com.google.api.gax.grpc.FlowControlSettings; +import com.google.api.gax.grpc.FlowController; import com.google.api.gax.core.RetrySettings; import com.google.api.gax.grpc.BundlingSettings; import com.google.api.gax.grpc.ChannelProvider; @@ -122,7 +123,7 @@ public static long getApiMaxRequestBytes() { private final RetrySettings retrySettings; private final LongRandom longRandom; - private final FlowController.Settings flowControlSettings; + private final FlowControlSettings flowControlSettings; private final boolean failOnFlowControlLimits; private final Lock messagesBundleLock; @@ -446,7 +447,7 @@ private long getMaxBundleBytes() { * The bundling settings configured on this {@code Publisher}. See {@link * #failOnFlowControlLimits()}. */ - public FlowController.Settings getFlowControlSettings() { + public FlowControlSettings getFlowControlSettings() { return flowControlSettings; } @@ -571,7 +572,7 @@ public long nextLong(long least, long bound) { BundlingSettings bundlingSettings = DEFAULT_BUNDLING_SETTINGS; // Client-side flow control options - FlowController.Settings flowControlSettings = FlowController.Settings.DEFAULT; + FlowControlSettings flowControlSettings = FlowControlSettings.getDefaultInstance(); boolean failOnFlowControlLimits = false; RetrySettings retrySettings = DEFAULT_RETRY_SETTINGS; @@ -606,17 +607,6 @@ public Builder setBundlingSettings(BundlingSettings bundlingSettings) { Preconditions.checkArgument(bundlingSettings.getRequestByteThreshold() > 0); Preconditions.checkNotNull(bundlingSettings.getDelayThreshold()); Preconditions.checkArgument(bundlingSettings.getDelayThreshold().getMillis() > 0); - - Preconditions.checkArgument( - bundlingSettings.getElementCountLimit() == null, - "elementCountLimit option not honored by current implementation"); - Preconditions.checkArgument( - bundlingSettings.getRequestByteLimit() == null, - "requestByteLimit option not honored by current implementation"); - Preconditions.checkArgument( - bundlingSettings.getBlockingCallCountThreshold() == null, - "blockingCallCountThreshold option not honored by current implementation"); - this.bundlingSettings = bundlingSettings; return this; } @@ -624,7 +614,7 @@ public Builder setBundlingSettings(BundlingSettings bundlingSettings) { // Flow control options /** Sets the flow control settings. */ - public Builder setFlowControlSettings(FlowController.Settings flowControlSettings) { + public Builder setFlowControlSettings(FlowControlSettings flowControlSettings) { this.flowControlSettings = Preconditions.checkNotNull(flowControlSettings); return this; } diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PublisherClient.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PublisherClient.java index 0b7ca15db947..e159bf016dc3 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PublisherClient.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/PublisherClient.java @@ -300,8 +300,7 @@ public final UnaryCallable createTopicCallable() { * @param messages The messages to publish. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ - /* package-private */ final PublishResponse publish( - TopicName topic, List messages) { + public final PublishResponse publish(TopicName topic, List messages) { PublishRequest request = PublishRequest.newBuilder().setTopicWithTopicName(topic).addAllMessages(messages).build(); @@ -365,7 +364,7 @@ public final PublishResponse publish(PublishRequest request) { * } *
    */ - /* package-private */ final UnaryCallable publishCallable() { + public final UnaryCallable publishCallable() { return publishCallable; } diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/StreamingSubscriberConnection.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/StreamingSubscriberConnection.java index 59e97b7d0666..18dec23151ad 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/StreamingSubscriberConnection.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/StreamingSubscriberConnection.java @@ -18,7 +18,7 @@ import static com.google.cloud.pubsub.spi.v1.StatusUtil.isRetryable; -import com.google.api.gax.bundling.FlowController; +import com.google.api.gax.grpc.FlowController; import com.google.api.stats.Distribution; import com.google.auth.Credentials; import com.google.cloud.Clock; diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/Subscriber.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/Subscriber.java index 7a39b7c8c5a9..84840c5d24eb 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/Subscriber.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/Subscriber.java @@ -16,7 +16,8 @@ package com.google.cloud.pubsub.spi.v1; -import com.google.api.gax.bundling.FlowController; +import com.google.api.gax.grpc.FlowControlSettings; +import com.google.api.gax.grpc.FlowController; import com.google.api.gax.grpc.ExecutorProvider; import com.google.api.gax.grpc.InstantiatingExecutorProvider; import com.google.api.stats.Distribution; @@ -148,7 +149,7 @@ public Duration getAckExpirationPadding() { } /** The flow control settings the Subscriber is configured with. */ - public FlowController.Settings getFlowControlSettings() { + public FlowControlSettings getFlowControlSettings() { return impl.flowControlSettings; } @@ -265,7 +266,7 @@ private static class SubscriberImpl extends AbstractService { private final SubscriptionName subscriptionName; private final String cachedSubscriptionNameString; - private final FlowController.Settings flowControlSettings; + private final FlowControlSettings flowControlSettings; private final Duration ackExpirationPadding; private final ScheduledExecutorService executor; private final Distribution ackLatencyDistribution = @@ -533,7 +534,7 @@ public static final class Builder { Duration ackExpirationPadding = DEFAULT_ACK_EXPIRATION_PADDING; - FlowController.Settings flowControlSettings = FlowController.Settings.DEFAULT; + FlowControlSettings flowControlSettings = FlowControlSettings.getDefaultInstance(); ExecutorProvider executorProvider = DEFAULT_EXECUTOR_PROVIDER; Optional>> channelBuilder = @@ -569,7 +570,7 @@ public Builder setChannelBuilder( } /** Sets the flow control settings. */ - public Builder setFlowControlSettings(FlowController.Settings flowControlSettings) { + public Builder setFlowControlSettings(FlowControlSettings flowControlSettings) { this.flowControlSettings = Preconditions.checkNotNull(flowControlSettings); return this; } diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/SubscriberClient.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/SubscriberClient.java index 23f723a79a31..53cf5d9d2f04 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/SubscriberClient.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/spi/v1/SubscriberClient.java @@ -814,7 +814,7 @@ public final UnaryCallable acknowledgeCallable() { * may return fewer than the number specified. * @throws com.google.api.gax.grpc.ApiException if the remote call fails */ - /* package-private */ final PullResponse pull( + public final PullResponse pull( SubscriptionName subscription, boolean returnImmediately, int maxMessages) { PullRequest request = @@ -875,7 +875,7 @@ public final PullResponse pull(PullRequest request) { * } *
  • */ - /* package-private */ final UnaryCallable pullCallable() { + public final UnaryCallable pullCallable() { return pullCallable; } @@ -927,7 +927,7 @@ public final PullResponse pull(PullRequest request) { * } *
    */ - /* package-private */ final StreamingCallable + public final StreamingCallable streamingPullCallable() { return streamingPullCallable; } diff --git a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/testing/LocalPubSubHelper.java b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/testing/LocalPubSubHelper.java index b99eb2ab2885..67ee55e2a870 100644 --- a/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/testing/LocalPubSubHelper.java +++ b/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/testing/LocalPubSubHelper.java @@ -16,12 +16,16 @@ package com.google.cloud.pubsub.testing; -import com.google.cloud.ServiceOptions; +import com.google.cloud.NoCredentials; +import com.google.cloud.RetryParams; +import com.google.cloud.pubsub.PubSubOptions; import com.google.cloud.testing.BaseEmulatorHelper; import com.google.common.collect.ImmutableList; + import io.grpc.ManagedChannel; import io.grpc.netty.NegotiationType; import io.grpc.netty.NettyChannelBuilder; + import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; @@ -31,12 +35,13 @@ import java.util.UUID; import java.util.concurrent.TimeoutException; import java.util.logging.Logger; + import org.joda.time.Duration; /** * A class that runs a Pubsub emulator instance for use in tests. */ -public class LocalPubSubHelper extends BaseEmulatorHelper { +public class LocalPubSubHelper extends BaseEmulatorHelper { private final List emulatorRunners; @@ -99,10 +104,18 @@ public ManagedChannel createChannel() { .build(); } - /** Returns a {@link ServiceOptions} describing the emulator. */ + /** + * Returns a {@link PubSubOptions} instance that sets the host to use the PubSub emulator on + * localhost. + */ @Override - public ServiceOptions getOptions() { - throw new UnsupportedOperationException("not implemented as PubSubOptions no longer exists"); + public PubSubOptions getOptions() { + return PubSubOptions.newBuilder() + .setProjectId(getProjectId()) + .setHost(DEFAULT_HOST + ":" + getPort()) + .setCredentials(NoCredentials.getInstance()) + .setRetryParams(RetryParams.noRetries()) + .build(); } /** diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/AckDeadlineRenewerTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/AckDeadlineRenewerTest.java new file mode 100644 index 000000000000..dbd1df110308 --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/AckDeadlineRenewerTest.java @@ -0,0 +1,313 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static org.junit.Assert.assertTrue; + +import com.google.cloud.GrpcServiceOptions.ExecutorFactory; +import com.google.cloud.pubsub.spi.v1.FakeScheduledExecutorService; +import com.google.common.collect.ImmutableList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import org.easymock.EasyMock; +import org.easymock.IAnswer; +import org.joda.time.Duration; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; + +public class AckDeadlineRenewerTest { + + private static final int MIN_DEADLINE_MILLIS = 10_000; + private static final Duration TIME_ADVANCE = Duration.millis(9_000); + + private static final String SUBSCRIPTION1 = "subscription1"; + private static final String SUBSCRIPTION2 = "subscription2"; + private static final String ACK_ID1 = "ack-id1"; + private static final String ACK_ID2 = "ack-id2"; + private static final String ACK_ID3 = "ack-id3"; + + private PubSub pubsub; + private FakeScheduledExecutorService executorService; + private AckDeadlineRenewer ackDeadlineRenewer; + + @Rule + public Timeout globalTimeout = Timeout.seconds(60); + + @Before + public void setUp() { + pubsub = EasyMock.createStrictMock(PubSub.class); + executorService = new FakeScheduledExecutorService(); + ExecutorFactory executorFactory = new ExecutorFactory() { + @Override + public ExecutorService get() { + return executorService; + } + @Override + public void release(ExecutorService executor) { + executorService.shutdown(); + } + }; + PubSubOptions options = + PubSubOptions.newBuilder() + .setProjectId("projectId") + .setExecutorFactory(executorFactory) + .setClock(executorService.getClock()) + .build(); + EasyMock.expect(pubsub.getOptions()).andReturn(options); + EasyMock.replay(pubsub); + ackDeadlineRenewer = new AckDeadlineRenewer(pubsub); + } + + @After + public void tearDown() throws Exception { + EasyMock.verify(pubsub); + ackDeadlineRenewer.close(); + } + + private IAnswer> createAnswer(final CountDownLatch latch, + final AtomicLong renewal) { + return new IAnswer>() { + @Override + public Future answer() throws Throwable { + latch.countDown(); + renewal.set(executorService.getClock().millis()); + return null; + } + }; + } + + @Test + public void testAddOneMessage() throws InterruptedException { + EasyMock.reset(pubsub); + final CountDownLatch firstLatch = new CountDownLatch(1); + final CountDownLatch secondLatch = new CountDownLatch(1); + final AtomicLong firstRenewal = new AtomicLong(); + final AtomicLong secondRenewal = new AtomicLong(); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION1, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1))) + .andAnswer(createAnswer(firstLatch, firstRenewal)); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION1, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1))) + .andAnswer(createAnswer(secondLatch, secondRenewal)); + EasyMock.replay(pubsub); + long addTime = executorService.getClock().millis(); + ackDeadlineRenewer.add(SUBSCRIPTION1, ACK_ID1); + executorService.advanceTime(TIME_ADVANCE); + firstLatch.await(); + assertTrue(firstRenewal.get() < (addTime + MIN_DEADLINE_MILLIS)); + executorService.advanceTime(TIME_ADVANCE); + secondLatch.await(); + assertTrue(secondRenewal.get() < (firstRenewal.get() + MIN_DEADLINE_MILLIS)); + } + + @Test + public void testAddMessages() throws InterruptedException { + EasyMock.reset(pubsub); + final CountDownLatch firstLatch = new CountDownLatch(2); + final CountDownLatch secondLatch = new CountDownLatch(2); + final AtomicLong firstRenewalSub1 = new AtomicLong(); + final AtomicLong firstRenewalSub2 = new AtomicLong(); + final AtomicLong secondRenewalSub1 = new AtomicLong(); + final AtomicLong secondRenewalSub2 = new AtomicLong(); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION1, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1, ACK_ID2))) + .andAnswer(createAnswer(firstLatch, firstRenewalSub1)); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION2, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1))) + .andAnswer(createAnswer(firstLatch, firstRenewalSub2)); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION1, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1, ACK_ID2))) + .andAnswer(createAnswer(secondLatch, secondRenewalSub1)); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION2, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1, ACK_ID3))) + .andAnswer(createAnswer(secondLatch, secondRenewalSub2)); + EasyMock.replay(pubsub); + long addTime1 = executorService.getClock().millis(); + ackDeadlineRenewer.add(SUBSCRIPTION1, ImmutableList.of(ACK_ID1, ACK_ID2)); + ackDeadlineRenewer.add(SUBSCRIPTION2, ACK_ID1); + executorService.advanceTime(TIME_ADVANCE); + firstLatch.await(); + assertTrue(firstRenewalSub1.get() < (addTime1 + MIN_DEADLINE_MILLIS)); + assertTrue(firstRenewalSub2.get() < (addTime1 + MIN_DEADLINE_MILLIS)); + ackDeadlineRenewer.add(SUBSCRIPTION2, ACK_ID3); + executorService.advanceTime(TIME_ADVANCE); + secondLatch.await(); + assertTrue(secondRenewalSub1.get() < (firstRenewalSub1.get() + MIN_DEADLINE_MILLIS)); + assertTrue(secondRenewalSub2.get() < (firstRenewalSub2.get() + MIN_DEADLINE_MILLIS)); + } + + @Test + public void testAddExistingMessage() throws InterruptedException { + EasyMock.reset(pubsub); + final CountDownLatch firstLatch = new CountDownLatch(2); + final CountDownLatch secondLatch = new CountDownLatch(2); + final AtomicLong firstRenewalSub1 = new AtomicLong(); + final AtomicLong firstRenewalSub2 = new AtomicLong(); + final AtomicLong secondRenewalSub1 = new AtomicLong(); + final AtomicLong secondRenewalSub2 = new AtomicLong(); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION1, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1, ACK_ID2))) + .andAnswer(createAnswer(firstLatch, firstRenewalSub1)); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION2, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1))) + .andAnswer(createAnswer(firstLatch, firstRenewalSub2)); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION1, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1, ACK_ID2))) + .andAnswer(createAnswer(secondLatch, secondRenewalSub1)); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION2, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1))) + .andAnswer(createAnswer(secondLatch, secondRenewalSub2)); + EasyMock.replay(pubsub); + long addTime1 = executorService.getClock().millis(); + ackDeadlineRenewer.add(SUBSCRIPTION1, ImmutableList.of(ACK_ID1, ACK_ID2)); + ackDeadlineRenewer.add(SUBSCRIPTION2, ACK_ID1); + executorService.advanceTime(TIME_ADVANCE); + firstLatch.await(); + assertTrue(firstRenewalSub1.get() < (addTime1 + MIN_DEADLINE_MILLIS)); + assertTrue(firstRenewalSub2.get() < (addTime1 + MIN_DEADLINE_MILLIS)); + ackDeadlineRenewer.add(SUBSCRIPTION2, ACK_ID1); + executorService.advanceTime(TIME_ADVANCE); + secondLatch.await(); + assertTrue(secondRenewalSub1.get() < (firstRenewalSub1.get() + MIN_DEADLINE_MILLIS)); + assertTrue(secondRenewalSub2.get() < (firstRenewalSub2.get() + MIN_DEADLINE_MILLIS)); + } + + @Test + public void testRemoveNonExistingMessage() throws InterruptedException { + EasyMock.reset(pubsub); + final CountDownLatch firstLatch = new CountDownLatch(2); + final CountDownLatch secondLatch = new CountDownLatch(2); + final AtomicLong firstRenewalSub1 = new AtomicLong(); + final AtomicLong firstRenewalSub2 = new AtomicLong(); + final AtomicLong secondRenewalSub1 = new AtomicLong(); + final AtomicLong secondRenewalSub2 = new AtomicLong(); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION1, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1, ACK_ID2))) + .andAnswer(createAnswer(firstLatch, firstRenewalSub1)); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION2, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1))) + .andAnswer(createAnswer(firstLatch, firstRenewalSub2)); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION1, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1, ACK_ID2))) + .andAnswer(createAnswer(secondLatch, secondRenewalSub1)); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION2, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1))) + .andAnswer(createAnswer(secondLatch, secondRenewalSub2)); + EasyMock.replay(pubsub); + long addTime1 = executorService.getClock().millis(); + ackDeadlineRenewer.add(SUBSCRIPTION1, ImmutableList.of(ACK_ID1, ACK_ID2)); + ackDeadlineRenewer.add(SUBSCRIPTION2, ACK_ID1); + executorService.advanceTime(TIME_ADVANCE); + firstLatch.await(); + assertTrue(firstRenewalSub1.get() < (addTime1 + MIN_DEADLINE_MILLIS)); + assertTrue(firstRenewalSub2.get() < (addTime1 + MIN_DEADLINE_MILLIS)); + ackDeadlineRenewer.remove(SUBSCRIPTION1, ACK_ID3); + executorService.advanceTime(TIME_ADVANCE); + secondLatch.await(); + assertTrue(secondRenewalSub1.get() < (firstRenewalSub1.get() + MIN_DEADLINE_MILLIS)); + assertTrue(secondRenewalSub2.get() < (firstRenewalSub2.get() + MIN_DEADLINE_MILLIS)); + } + + @Test + public void testRemoveMessage() throws InterruptedException { + EasyMock.reset(pubsub); + final CountDownLatch firstLatch = new CountDownLatch(2); + final CountDownLatch secondLatch = new CountDownLatch(2); + final AtomicLong firstRenewalSub1 = new AtomicLong(); + final AtomicLong firstRenewalSub2 = new AtomicLong(); + final AtomicLong secondRenewalSub1 = new AtomicLong(); + final AtomicLong secondRenewalSub2 = new AtomicLong(); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION1, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1, ACK_ID2))) + .andAnswer(createAnswer(firstLatch, firstRenewalSub1)); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION2, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1))) + .andAnswer(createAnswer(firstLatch, firstRenewalSub2)); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION1, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1))) + .andAnswer(createAnswer(secondLatch, secondRenewalSub1)); + EasyMock.expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION2, MIN_DEADLINE_MILLIS, + TimeUnit.MILLISECONDS, ImmutableList.of(ACK_ID1))) + .andAnswer(createAnswer(secondLatch, secondRenewalSub2)); + EasyMock.replay(pubsub); + long addTime1 = executorService.getClock().millis(); + ackDeadlineRenewer.add(SUBSCRIPTION1, ImmutableList.of(ACK_ID1, ACK_ID2)); + ackDeadlineRenewer.add(SUBSCRIPTION2, ACK_ID1); + executorService.advanceTime(TIME_ADVANCE); + firstLatch.await(); + assertTrue(firstRenewalSub1.get() < (addTime1 + MIN_DEADLINE_MILLIS)); + assertTrue(firstRenewalSub2.get() < (addTime1 + MIN_DEADLINE_MILLIS)); + ackDeadlineRenewer.remove(SUBSCRIPTION1, ACK_ID2); + executorService.advanceTime(TIME_ADVANCE); + secondLatch.await(); + assertTrue(secondRenewalSub1.get() < (firstRenewalSub1.get() + MIN_DEADLINE_MILLIS)); + assertTrue(secondRenewalSub2.get() < (firstRenewalSub2.get() + MIN_DEADLINE_MILLIS)); + } + + @Test + @SuppressWarnings("unchecked") + public void testClose() throws Exception { + PubSub pubsub = EasyMock.createStrictMock(PubSub.class); + ScheduledExecutorService executor = EasyMock.createStrictMock(ScheduledExecutorService.class); + ExecutorFactory executorFactory = EasyMock.createStrictMock(ExecutorFactory.class); + EasyMock.expect(executorFactory.get()).andReturn(executor); + PubSubOptions options = PubSubOptions.newBuilder() + .setProjectId("projectId") + .setExecutorFactory(executorFactory) + .build(); + EasyMock.expect(pubsub.getOptions()).andReturn(options); + executorFactory.release(executor); + EasyMock.expectLastCall(); + EasyMock.replay(executor, executorFactory, pubsub); + AckDeadlineRenewer ackDeadlineRenewer = new AckDeadlineRenewer(pubsub); + ackDeadlineRenewer.close(); + EasyMock.verify(pubsub, executor, executorFactory); + } + + @Test + @SuppressWarnings("unchecked") + public void testCloseWithMessage() throws Exception { + PubSub pubsub = EasyMock.createStrictMock(PubSub.class); + ScheduledExecutorService executor = EasyMock.createStrictMock(ScheduledExecutorService.class); + ExecutorFactory executorFactory = EasyMock.createStrictMock(ExecutorFactory.class); + EasyMock.expect(executorFactory.get()).andReturn(executor); + ScheduledFuture future = EasyMock.createStrictMock(ScheduledFuture.class); + EasyMock.expect(executor.schedule(EasyMock.anyObject(), EasyMock.anyLong(), + EasyMock.eq(TimeUnit.MILLISECONDS))).andReturn(future); + PubSubOptions options = PubSubOptions.newBuilder() + .setProjectId("projectId") + .setExecutorFactory(executorFactory) + .build(); + EasyMock.expect(pubsub.getOptions()).andReturn(options); + EasyMock.expect(future.cancel(true)).andReturn(true); + executorFactory.release(executor); + EasyMock.expectLastCall(); + EasyMock.replay(executor, executorFactory, future, pubsub); + AckDeadlineRenewer ackDeadlineRenewer = new AckDeadlineRenewer(pubsub); + ackDeadlineRenewer.add(SUBSCRIPTION1, ACK_ID1); + ackDeadlineRenewer.close(); + EasyMock.verify(pubsub, executor, executorFactory, future); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/BaseSystemTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/BaseSystemTest.java new file mode 100644 index 000000000000..d9524af5925e --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/BaseSystemTest.java @@ -0,0 +1,837 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.AsyncPage; +import com.google.cloud.Page; +import com.google.cloud.pubsub.PubSub.MessageConsumer; +import com.google.cloud.pubsub.PubSub.MessageProcessor; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * A base class for system tests. This class can be extended to run system tests in different + * environments (e.g. local emulator or remote Pub/Sub service). + */ +public abstract class BaseSystemTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + /** + * Returns the Pub/Sub service used to issue requests. This service can be such that it interacts + * with the remote Pub/Sub service (for integration tests) or with an emulator + * (for local testing). + */ + protected abstract PubSub pubsub(); + + /** + * Formats a resource name for testing purpose. For instance, for tests against the remote + * service, it is recommended to append to the name a random or time-based seed to prevent + * name clashes. + */ + protected abstract String formatForTest(String resourceName); + + @Test + public void testCreateGetAndDeleteTopic() { + String name = formatForTest("test-create-get-delete-topic"); + Topic topic = pubsub().create(TopicInfo.of(name)); + assertEquals(name, topic.getName()); + Topic remoteTopic = pubsub().getTopic(name); + assertEquals(topic, remoteTopic); + assertTrue(topic.delete()); + } + + @Test + public void testGetTopic_NotExist() { + String name = formatForTest("test-get-non-existing-topic"); + assertNull(pubsub().getTopic(name)); + } + + @Test + public void testDeleteTopic_NotExist() { + assertFalse(pubsub().deleteTopic(formatForTest("test-delete-non-existing-topic"))); + } + + @Test + public void testCreateGetAndDeleteTopicAsync() throws ExecutionException, InterruptedException { + String name = formatForTest("test-create-get-delete-async-topic"); + Future topicFuture = pubsub().createAsync(TopicInfo.of(name)); + Topic createdTopic = topicFuture.get(); + assertEquals(name, createdTopic.getName()); + topicFuture = pubsub().getTopicAsync(name); + assertEquals(createdTopic, topicFuture.get()); + assertTrue(createdTopic.deleteAsync().get()); + } + + @Test + public void testListTopics() { + Topic topic1 = pubsub().create(TopicInfo.of(formatForTest("test-list-topic1"))); + Topic topic2 = pubsub().create(TopicInfo.of(formatForTest("test-list-topic2"))); + Topic topic3 = pubsub().create(TopicInfo.of(formatForTest("test-list-topic3"))); + Set topicNames = Sets.newHashSet(); + // We use 1 as page size to force pagination + Page topics = pubsub().listTopics(PubSub.ListOption.pageSize(1)); + Iterator iterator = topics.iterateAll(); + while (iterator.hasNext()) { + topicNames.add(iterator.next().getName()); + } + assertTrue(topicNames.contains(topic1.getName())); + assertTrue(topicNames.contains(topic2.getName())); + assertTrue(topicNames.contains(topic3.getName())); + assertTrue(topic1.delete()); + assertTrue(topic2.delete()); + assertTrue(topic3.delete()); + } + + @Test + public void testListTopicsAsync() throws ExecutionException, InterruptedException { + Topic topic1 = pubsub().create(TopicInfo.of(formatForTest("test-list-async-topic1"))); + Topic topic2 = pubsub().create(TopicInfo.of(formatForTest("test-list-async-topic2"))); + Topic topic3 = pubsub().create(TopicInfo.of(formatForTest("test-list-async-topic3"))); + Set topicNames = Sets.newHashSet(); + Future> pageFuture = pubsub().listTopicsAsync(PubSub.ListOption.pageSize(1)); + Iterator iterator = pageFuture.get().iterateAll(); + while (iterator.hasNext()) { + topicNames.add(iterator.next().getName()); + } + assertTrue(topicNames.contains(topic1.getName())); + assertTrue(topicNames.contains(topic2.getName())); + assertTrue(topicNames.contains(topic3.getName())); + assertTrue(topic1.delete()); + assertTrue(topic2.delete()); + assertTrue(topic3.delete()); + } + + @Test + public void testPublishOneMessage() { + String topic = formatForTest("test-publish-one-message-topic"); + pubsub().create(TopicInfo.of(topic)); + Message message = Message.of("payload"); + assertNotNull(pubsub().publish(topic, message)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testPublishNonExistingTopic() { + String topic = formatForTest("test-publish-non-existing-topic"); + Message message = Message.of("payload"); + thrown.expect(PubSubException.class); + pubsub().publish(topic, message); + } + + @Test + public void testPublishOneMessageAsync() throws ExecutionException, InterruptedException { + String topic = formatForTest("test-publish-one-message-async-topic"); + pubsub().create(TopicInfo.of(topic)); + Message message = Message.of("payload"); + Future publishFuture = pubsub().publishAsync(topic, message); + assertNotNull(publishFuture.get()); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testPublishMoreMessages() { + String topic = formatForTest("test-publish-more-messages-topic"); + pubsub().create(TopicInfo.of(topic)); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + List messageIds = pubsub().publish(topic, message1, message2); + assertEquals(2, messageIds.size()); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testPublishMoreMessagesAsync() throws ExecutionException, InterruptedException { + String topic = formatForTest("test-publish-more-messages-topic-async-topic"); + pubsub().create(TopicInfo.of(topic)); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + Future> publishFuture = pubsub().publishAsync(topic, message1, message2); + assertEquals(2, publishFuture.get().size()); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testPublishMessageList() { + String topic = formatForTest("test-publish-message-list-topic"); + pubsub().create(TopicInfo.of(topic)); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + List messageIds = pubsub().publish(topic, ImmutableList.of(message1, message2)); + assertEquals(2, messageIds.size()); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testPublishMessagesListAsync() throws ExecutionException, InterruptedException { + String topic = formatForTest("test-publish-message-list-async-topic"); + pubsub().create(TopicInfo.of(topic)); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + Future> publishFuture = + pubsub().publishAsync(topic, ImmutableList.of(message1, message2)); + assertEquals(2, publishFuture.get().size()); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testCreateGetAndDeleteSubscription() { + String topic = formatForTest("test-create-get-delete-subscription-topic"); + pubsub().create(TopicInfo.of(topic)); + String name = formatForTest("test-create-get-delete-subscription"); + Subscription subscription = pubsub().create(SubscriptionInfo.of(topic, name)); + assertEquals(TopicId.of(pubsub().getOptions().getProjectId(), topic), subscription.getTopic()); + assertEquals(name, subscription.getName()); + assertNull(subscription.getPushConfig()); + // todo(mziccard) seems not to work on the emulator (returns 60) - see #989 + // assertEquals(10, subscription.ackDeadlineSeconds()); + Subscription remoteSubscription = pubsub().getSubscription(name); + assertEquals(subscription, remoteSubscription); + assertTrue(subscription.delete()); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testGetSubscription_NotExist() { + assertNull(pubsub().getSubscription(formatForTest("test-get-non-existing-subscription"))); + } + + @Test + public void testDeleteSubscription_NotExist() { + assertFalse( + pubsub().deleteSubscription(formatForTest("test-delete-non-existing-subscription"))); + } + + @Test + public void testCreateGetAndDeleteSubscriptionAsync() + throws ExecutionException, InterruptedException { + String topic = formatForTest("test-create-get-delete-async-subscription-topic"); + pubsub().create(TopicInfo.of(topic)); + String name = formatForTest("test-create-get-delete-async-subscription"); + String endpoint = "https://" + pubsub().getOptions().getProjectId() + ".appspot.com/push"; + PushConfig pushConfig = PushConfig.of(endpoint); + Future subscriptionFuture = pubsub().createAsync( + SubscriptionInfo.newBuilder(topic, name).setPushConfig(pushConfig).build()); + Subscription subscription = subscriptionFuture.get(); + assertEquals(TopicId.of(pubsub().getOptions().getProjectId(), topic), subscription.getTopic()); + assertEquals(name, subscription.getName()); + assertEquals(pushConfig, subscription.getPushConfig()); + // todo(mziccard) seems not to work on the emulator (returns 60) - see #989 + // assertEquals(10, subscription.ackDeadlineSeconds()); + subscriptionFuture = pubsub().getSubscriptionAsync(name); + Subscription remoteSubscription = subscriptionFuture.get(); + assertEquals(subscription, remoteSubscription); + assertTrue(subscription.deleteAsync().get()); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + @Ignore("Emulator incosistency; see issue ##988") + public void testGetSubscriptionDeletedTopic() { + String topic = formatForTest("test-get-deleted-topic-subscription-topic"); + pubsub().create(TopicInfo.of(topic)); + String name = formatForTest("test-get-deleted-topic-subscription"); + Subscription subscription = pubsub().create(SubscriptionInfo.of(topic, name)); + assertEquals(TopicId.of(pubsub().getOptions().getProjectId(), topic), subscription.getTopic()); + assertEquals(name, subscription.getName()); + assertNull(subscription.getPushConfig()); + // todo(mziccard) seems not to work on the emulator (returns 60) - see #989 + // assertEquals(10, subscription.ackDeadlineSeconds()); + assertTrue(pubsub().deleteTopic(topic)); + assertNull(pubsub().getTopic(topic)); + Subscription remoteSubscription = pubsub().getSubscription(name); + assertEquals(TopicId.of("_deleted-topic_"), remoteSubscription.getTopic()); + assertEquals(name, remoteSubscription.getName()); + assertNull(remoteSubscription.getPushConfig()); + assertTrue(subscription.delete()); + } + + @Test + public void testReplaceSubscriptionPushConfig() { + String topic = formatForTest("test-replace-push-config-topic"); + pubsub().create(TopicInfo.of(topic)); + String name = formatForTest("test-replace-push-config-subscription"); + String endpoint = "https://" + pubsub().getOptions().getProjectId() + ".appspot.com/push"; + PushConfig pushConfig = PushConfig.of(endpoint); + Subscription subscription = + pubsub().create(SubscriptionInfo.newBuilder(topic, name).setPushConfig(pushConfig).build()); + assertEquals(TopicId.of(pubsub().getOptions().getProjectId(), topic), subscription.getTopic()); + assertEquals(name, subscription.getName()); + assertEquals(pushConfig, subscription.getPushConfig()); + // todo(mziccard) seems not to work on the emulator (returns 60) - see #989 + // assertEquals(10, subscription.ackDeadlineSeconds()); + pubsub().replacePushConfig(name, null); + Subscription remoteSubscription = pubsub().getSubscription(name); + assertEquals(TopicId.of(pubsub().getOptions().getProjectId(), topic), + remoteSubscription.getTopic()); + assertEquals(name, remoteSubscription.getName()); + assertNull(remoteSubscription.getPushConfig()); + // todo(mziccard) seems not to work on the emulator (returns 60) - see #989 + // assertEquals(10, remoteSubscription.ackDeadlineSeconds()); + assertTrue(subscription.delete()); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testReplaceNonExistingSubscriptionPushConfig() { + String name = formatForTest("test-replace-push-config-non-existing-subscription"); + thrown.expect(PubSubException.class); + pubsub().replacePushConfig(name, null); + } + + @Test + public void testReplaceSubscriptionPushConfigAsync() + throws ExecutionException, InterruptedException { + String topic = formatForTest("test-replace-push-config-async-topic"); + pubsub().create(TopicInfo.of(topic)); + String name = formatForTest("test-replace-push-config-async-subscription"); + Future subscriptionFuture = + pubsub().createAsync(SubscriptionInfo.of(topic, name)); + Subscription subscription = subscriptionFuture.get(); + assertEquals(TopicId.of(pubsub().getOptions().getProjectId(), topic), subscription.getTopic()); + assertEquals(name, subscription.getName()); + assertNull(subscription.getPushConfig()); + // todo(mziccard) seems not to work on the emulator (returns 60) - see #989 + // assertEquals(10, subscription.ackDeadlineSeconds()); + String endpoint = "https://" + pubsub().getOptions().getProjectId() + ".appspot.com/push"; + PushConfig pushConfig = PushConfig.of(endpoint); + pubsub().replacePushConfigAsync(name, pushConfig).get(); + Subscription remoteSubscription = pubsub().getSubscriptionAsync(name).get(); + assertEquals(TopicId.of(pubsub().getOptions().getProjectId(), topic), + remoteSubscription.getTopic()); + assertEquals(name, remoteSubscription.getName()); + assertEquals(pushConfig, remoteSubscription.getPushConfig()); + // todo(mziccard) seems not to work on the emulator (returns 60) - see #989 + // assertEquals(10, remoteSubscription.ackDeadlineSeconds()); + assertTrue(subscription.deleteAsync().get()); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testListSubscriptions() { + String topicName1 = formatForTest("test-list-subscriptions-topic1"); + String topicName2 = formatForTest("test-list-subscriptions-topic2"); + Topic topic1 = pubsub().create(TopicInfo.of(topicName1)); + Topic topic2 = pubsub().create(TopicInfo.of(topicName2)); + String subscriptionName1 = formatForTest("test-list-subscriptions-subscription1"); + String subscriptionName2 = formatForTest("test-list-subscriptions-subscription2"); + String subscriptionName3 = formatForTest("test-list-subscriptions-subscription3"); + Subscription subscription1 = + pubsub().create(SubscriptionInfo.of(topicName1, subscriptionName1)); + Subscription subscription2 = + pubsub().create(SubscriptionInfo.of(topicName1, subscriptionName2)); + Subscription subscription3 = + pubsub().create(SubscriptionInfo.of(topicName2, subscriptionName3)); + Set subscriptionNames = Sets.newHashSet(); + // We use 1 as page size to force pagination + Page subscriptions = pubsub().listSubscriptions(PubSub.ListOption.pageSize(1)); + Iterator iterator = subscriptions.iterateAll(); + while (iterator.hasNext()) { + String name = iterator.next().getName(); + subscriptionNames.add(name); + } + assertTrue(subscriptionNames.contains(subscriptionName1)); + assertTrue(subscriptionNames.contains(subscriptionName2)); + assertTrue(subscriptionNames.contains(subscriptionName3)); + Set topicSubscriptionNames = Sets.newHashSet(); + Page topic1Subscriptions = + topic1.listSubscriptions(PubSub.ListOption.pageSize(1)); + Iterator firstStringPageIterator = topic1Subscriptions.getValues().iterator(); + topicSubscriptionNames.add(firstStringPageIterator.next().getSubscription()); + assertFalse(firstStringPageIterator.hasNext()); + Iterator topicSubscriptionsIterator = + topic1Subscriptions.getNextPage().iterateAll(); + while (topicSubscriptionsIterator.hasNext()) { + topicSubscriptionNames.add(topicSubscriptionsIterator.next().getSubscription()); + } + assertEquals(2, topicSubscriptionNames.size()); + assertTrue(topicSubscriptionNames.contains(subscriptionName1)); + assertTrue(topicSubscriptionNames.contains(subscriptionName2)); + assertTrue(topic1.delete()); + assertTrue(topic2.delete()); + assertTrue(subscription1.delete()); + assertTrue(subscription2.delete()); + assertTrue(subscription3.delete()); + } + + @Test + public void testListSubscriptionsAsync() throws ExecutionException, InterruptedException { + String topicName1 = formatForTest("test-list-subscriptions-async-topic1"); + String topicName2 = formatForTest("test-list-subscriptions-async-topic2"); + Topic topic1 = pubsub().create(TopicInfo.of(topicName1)); + Topic topic2 = pubsub().create(TopicInfo.of(topicName2)); + String subscriptionName1 = formatForTest("test-list-subscriptions-async-subscription1"); + String subscriptionName2 = formatForTest("test-list-subscriptions-async-subscription2"); + String subscriptionName3 = formatForTest("test-list-subscriptions-async-subscription3"); + Subscription subscription1 = + pubsub().create(SubscriptionInfo.of(topicName1, subscriptionName1)); + Subscription subscription2 = + pubsub().create(SubscriptionInfo.of(topicName1, subscriptionName2)); + Subscription subscription3 = + pubsub().create(SubscriptionInfo.of(topicName2, subscriptionName3)); + // We use 1 as page size to force pagination + Set subscriptionNames = Sets.newHashSet(); + Future> pageFuture = + pubsub().listSubscriptionsAsync(PubSub.ListOption.pageSize(1)); + Iterator iterator = pageFuture.get().iterateAll(); + while (iterator.hasNext()) { + subscriptionNames.add(iterator.next().getName()); + } + assertTrue(subscriptionNames.contains(subscriptionName1)); + assertTrue(subscriptionNames.contains(subscriptionName2)); + assertTrue(subscriptionNames.contains(subscriptionName3)); + Set topicSubscriptionNames = Sets.newHashSet(); + AsyncPage topic1Subscriptions = + topic1.listSubscriptionsAsync(PubSub.ListOption.pageSize(1)).get(); + Iterator firstStringPageIterator = topic1Subscriptions.getValues().iterator(); + topicSubscriptionNames.add(firstStringPageIterator.next().getSubscription()); + assertFalse(firstStringPageIterator.hasNext()); + Iterator topicSubscriptionsIterator = + topic1Subscriptions.getNextPageAsync().get().iterateAll(); + while (topicSubscriptionsIterator.hasNext()) { + topicSubscriptionNames.add(topicSubscriptionsIterator.next().getSubscription()); + } + assertEquals(2, topicSubscriptionNames.size()); + assertTrue(topicSubscriptionNames.contains(subscriptionName1)); + assertTrue(topicSubscriptionNames.contains(subscriptionName2)); + assertTrue(topic1.delete()); + assertTrue(topic2.delete()); + assertTrue(subscription1.delete()); + assertTrue(subscription2.delete()); + assertTrue(subscription3.delete()); + } + + @Test + public void testPullMessages() { + String topic = formatForTest("test-pull-messages-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-pull-messages-subscription"); + pubsub().create(SubscriptionInfo.of(topic, subscription)); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + List messageIds = pubsub().publish(topic, ImmutableList.of(message1, message2)); + assertEquals(2, messageIds.size()); + Iterator iterator = pubsub().pull(subscription, 2); + assertEquals(message1.getPayloadAsString(), iterator.next().getPayloadAsString()); + assertEquals(message2.getPayloadAsString(), iterator.next().getPayloadAsString()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testPullMessagesAndAutoRenewDeadline() throws InterruptedException { + String topic = formatForTest("test-pull-messages-and-renew-deadline-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-pull-messages-and-renew-deadline-subscription"); + pubsub().create( + SubscriptionInfo.newBuilder(topic, subscription).setAckDeadLineSeconds(10).build()); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + // todo(mziccard): use batch publish if #1017 gets fixed, or remove this comment + pubsub().publish(topic, message1); + pubsub().publish(topic, message2); + Iterator iterator = pubsub().pull(subscription, 2); + while (!iterator.hasNext()) { + Thread.sleep(500); + iterator = pubsub().pull(subscription, 2); + } + ReceivedMessage consumedMessage = iterator.next(); + if (!iterator.hasNext()) { + iterator = pubsub().pull(subscription, 1); + while (!iterator.hasNext()) { + Thread.sleep(500); + iterator = pubsub().pull(subscription, 1); + } + } + Thread.sleep(15000); + // first message was consumed while second message is still being renewed + Iterator nextIterator = pubsub().pull(subscription, 2); + assertTrue(nextIterator.hasNext()); + ReceivedMessage message = nextIterator.next(); + assertEquals(consumedMessage.getPayloadAsString(), message.getPayloadAsString()); + assertFalse(nextIterator.hasNext()); + consumedMessage.ack(); + iterator.next().ack(); + nextIterator = pubsub().pull(subscription, 2); + assertFalse(nextIterator.hasNext()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testPullMessagesAndModifyAckDeadline() throws InterruptedException { + String topic = formatForTest("test-pull-messages-and-modify-deadline-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-pull-messages-and-modify-deadline-subscription"); + pubsub().create( + SubscriptionInfo.newBuilder(topic, subscription).setAckDeadLineSeconds(10).build()); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + // todo(mziccard): use batch publish if #1017 gets fixed, or remove this comment + pubsub().publish(topic, message1); + pubsub().publish(topic, message2); + // Consume all messages and stop ack renewal + List receivedMessages = Lists.newArrayList(pubsub().pull(subscription, 2)); + while (receivedMessages.size() < 2) { + Thread.sleep(500); + Iterators.addAll(receivedMessages, pubsub().pull(subscription, 2)); + } + receivedMessages.get(0).modifyAckDeadline(60, TimeUnit.SECONDS); + Thread.sleep(15000); + // first message was renewed while second message should still be sent on pull requests + Iterator nextIterator = pubsub().pull(subscription, 2); + assertTrue(nextIterator.hasNext()); + ReceivedMessage message = nextIterator.next(); + assertEquals(receivedMessages.get(1).getPayloadAsString(), message.getPayloadAsString()); + assertFalse(nextIterator.hasNext()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testPullNonExistingSubscription() { + thrown.expect(PubSubException.class); + pubsub().pull(formatForTest("non-existing-subscription"), 2); + } + + @Test + public void testPullMessagesAsync() throws ExecutionException, InterruptedException { + String topic = formatForTest("test-pull-messages-async-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-pull-messages-async-subscription"); + pubsub().create(SubscriptionInfo.of(topic, subscription)); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + List messageIds = pubsub().publish(topic, ImmutableList.of(message1, message2)); + assertEquals(2, messageIds.size()); + Iterator iterator = pubsub().pullAsync(subscription, 2).get(); + assertEquals(message1.getPayloadAsString(), iterator.next().getPayloadAsString()); + assertEquals(message2.getPayloadAsString(), iterator.next().getPayloadAsString()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testPullMessagesAsyncNonImmediately() throws ExecutionException, InterruptedException { + String topic = formatForTest("test-pull-messages-async-non-immediately-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-pull-messages-async-subscription"); + pubsub().create(SubscriptionInfo.of(topic, subscription)); + Future> future = pubsub().pullAsync(subscription, 2); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + List messageIds = pubsub().publish(topic, ImmutableList.of(message1, message2)); + assertEquals(2, messageIds.size()); + Iterator iterator = future.get(); + assertEquals(message1.getPayloadAsString(), iterator.next().getPayloadAsString()); + assertEquals(message2.getPayloadAsString(), iterator.next().getPayloadAsString()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testPullAsyncNonExistingSubscription() + throws ExecutionException, InterruptedException { + thrown.expect(ExecutionException.class); + pubsub().pullAsync(formatForTest("non-existing-subscription"), 2).get(); + } + + @Test + public void testMessageConsumer() throws Exception { + String topic = formatForTest("test-message-consumer-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-message-consumer-subscription"); + pubsub().create(SubscriptionInfo.of(topic, subscription)); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + Set payloads = Sets.newHashSet("payload1", "payload2"); + List messageIds = pubsub().publish(topic, ImmutableList.of(message1, message2)); + assertEquals(2, messageIds.size()); + final List receivedMessages = Collections.synchronizedList(new ArrayList()); + final CountDownLatch countDownLatch = new CountDownLatch(2); + MessageProcessor processor = new MessageProcessor() { + @Override + public void process(Message message) throws Exception { + receivedMessages.add(message); + countDownLatch.countDown(); + } + }; + try(MessageConsumer consumer = pubsub().pullAsync(subscription, processor)) { + countDownLatch.await(); + } + for (Message message : receivedMessages) { + payloads.contains(message.getPayloadAsString()); + } + // Messages have all been acked, they should not be pulled again + Iterator messages = pubsub().pull(subscription, 2); + assertFalse(messages.hasNext()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testMessageConsumerNack() throws Exception { + String topic = formatForTest("test-message-consumer-nack-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-message-consumer-nack-subscription"); + pubsub().create(SubscriptionInfo.of(topic, subscription)); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + Set payloads = Sets.newHashSet("payload1", "payload2"); + List messageIds = pubsub().publish(topic, ImmutableList.of(message1, message2)); + assertEquals(2, messageIds.size()); + final List receivedMessages = Collections.synchronizedList(new ArrayList()); + final CountDownLatch countDownLatch = new CountDownLatch(2); + MessageProcessor processor = new MessageProcessor() { + @Override + public void process(Message message) throws Exception { + receivedMessages.add(message); + countDownLatch.countDown(); + throw new RuntimeException("Force nack"); + } + }; + try (MessageConsumer consumer = pubsub().pullAsync(subscription, processor)) { + countDownLatch.await(); + } + for (Message message : receivedMessages) { + payloads.contains(message.getPayloadAsString()); + } + // Messages have all been nacked, we should be able to pull them again + Thread.sleep(5000); + Iterator messages = pubsub().pull(subscription, 2); + while (messages.hasNext()) { + payloads.contains(messages.next().getPayloadAsString()); + } + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testMessageConsumerWithMoreMessages() throws Exception { + String topic = formatForTest("test-message-consumer-more-messages-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-message-consumer-more-messages-subscriptions"); + pubsub().create(SubscriptionInfo.of(topic, subscription)); + int totalMessages = 200; + Set payloads = Sets.newHashSetWithExpectedSize(totalMessages); + List messagesToSend = Lists.newArrayListWithCapacity(totalMessages); + for (int i = 0; i < totalMessages; i++) { + String payload = "payload" + i; + messagesToSend.add(Message.of(payload)); + payloads.add(payload); + + } + List messageIds = pubsub().publish(topic, messagesToSend); + assertEquals(totalMessages, messageIds.size()); + final List receivedMessages = Collections.synchronizedList(new ArrayList()); + final CountDownLatch countDownLatch = new CountDownLatch(totalMessages); + MessageProcessor processor = new MessageProcessor() { + @Override + public void process(Message message) throws Exception { + receivedMessages.add(message); + countDownLatch.countDown(); + } + }; + try(MessageConsumer consumer = pubsub().pullAsync(subscription, processor)) { + countDownLatch.await(); + } + // Messages have all been acked, they should not be pulled again + Iterator messages = pubsub().pull(subscription, totalMessages); + assertFalse(messages.hasNext()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testMessageConsumerAndAutoRenewDeadline() throws Exception { + String topic = formatForTest("test-message-consumer-and-renew-deadline-topic"); + pubsub().create(TopicInfo.of(topic)); + final String subscription = + formatForTest("test-message-consumer-and-renew-deadline-subscription"); + pubsub().create( + SubscriptionInfo.newBuilder(topic, subscription).setAckDeadLineSeconds(10).build()); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + Set payloads = Sets.newHashSet("payload1", "payload2"); + List messageIds = pubsub().publish(topic, ImmutableList.of(message1, message2)); + assertEquals(2, messageIds.size()); + final List receivedMessages = Collections.synchronizedList(new ArrayList()); + final CountDownLatch countDownLatch = new CountDownLatch(2); + MessageProcessor processor = new MessageProcessor() { + @Override + public void process(Message message) throws Exception { + receivedMessages.add(message); + Thread.sleep(15000); + // message deadline is being renewed, it should not be pulled again + Iterator messages = pubsub().pull(subscription, 2); + assertFalse(messages.hasNext()); + countDownLatch.countDown(); + } + }; + try(MessageConsumer consumer = pubsub().pullAsync(subscription, processor)) { + countDownLatch.await(); + } + for (Message message : receivedMessages) { + payloads.contains(message.getPayloadAsString()); + } + // Messages have all been acked, they should not be pulled again + Iterator messages = pubsub().pull(subscription, 2); + assertFalse(messages.hasNext()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testAckAndNackOneMessage() { + String topic = formatForTest("test-ack-one-message-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-ack-one-message-subscription"); + pubsub().create(SubscriptionInfo.of(topic, subscription)); + Message message = Message.of("payload"); + assertNotNull(pubsub().publish(topic, message)); + Iterator receivedMessages = pubsub().pull(subscription, 1); + receivedMessages.next().nack(); + receivedMessages = pubsub().pull(subscription, 1); + receivedMessages.next().ack(); + assertFalse(pubsub().pull(subscription, 1).hasNext()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testAckAndNackOneMessageAsync() throws ExecutionException, InterruptedException { + String topic = formatForTest("test-ack-one-message-async-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-ack-one-message-async-subscription"); + pubsub().create(SubscriptionInfo.of(topic, subscription)); + Message message = Message.of("payload"); + assertNotNull(pubsub().publish(topic, message)); + Iterator receivedMessages = pubsub().pull(subscription, 1); + receivedMessages.next().nackAsync().get(); + receivedMessages = pubsub().pull(subscription, 1); + receivedMessages.next().ackAsync().get(); + assertFalse(pubsub().pull(subscription, 1).hasNext()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testAckAndNackMoreMessages() throws ExecutionException, InterruptedException { + String topic = formatForTest("test-ack-more-messages-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-ack-more-messages-subscription"); + pubsub().create(SubscriptionInfo.of(topic, subscription)); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + assertNotNull(pubsub().publish(topic, message1, message2)); + Iterator receivedMessages = pubsub().pull(subscription, 2); + pubsub().nack(subscription, receivedMessages.next().getAckId(), + receivedMessages.next().getAckId()); + receivedMessages = pubsub().pull(subscription, 2); + pubsub().ack(subscription, receivedMessages.next().getAckId(), + receivedMessages.next().getAckId()); + assertFalse(pubsub().pull(subscription, 2).hasNext()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testAckAndNackMoreMessagesAsync() throws ExecutionException, InterruptedException { + String topic = formatForTest("test-ack-more-messages-async-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-ack-more-messages-async-subscription"); + pubsub().create(SubscriptionInfo.of(topic, subscription)); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + assertNotNull(pubsub().publish(topic, message1, message2)); + Iterator receivedMessages = pubsub().pull(subscription, 2); + pubsub().nackAsync(subscription, receivedMessages.next().getAckId(), + receivedMessages.next().getAckId()) + .get(); + receivedMessages = pubsub().pull(subscription, 2); + pubsub().ackAsync(subscription, receivedMessages.next().getAckId(), + receivedMessages.next().getAckId()) + .get(); + assertFalse(pubsub().pull(subscription, 2).hasNext()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testAckAndNackMessageList() throws ExecutionException, InterruptedException { + String topic = formatForTest("test-ack-message-list-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-ack-message-list-subscription"); + pubsub().create(SubscriptionInfo.of(topic, subscription)); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + assertNotNull(pubsub().publish(topic, ImmutableList.of(message1, message2))); + Iterator receivedMessages = pubsub().pull(subscription, 2); + pubsub().nack(subscription, + ImmutableList.of(receivedMessages.next().getAckId(), receivedMessages.next().getAckId())); + receivedMessages = pubsub().pull(subscription, 2); + pubsub().ack(subscription, + ImmutableList.of(receivedMessages.next().getAckId(), receivedMessages.next().getAckId())); + assertFalse(pubsub().pull(subscription, 2).hasNext()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } + + @Test + public void testAckAndNackMessageListAsync() throws ExecutionException, InterruptedException { + String topic = formatForTest("test-ack-message-list-async-topic"); + pubsub().create(TopicInfo.of(topic)); + String subscription = formatForTest("test-ack-message-list-async-subscription"); + pubsub().create(SubscriptionInfo.of(topic, subscription)); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + assertNotNull(pubsub().publish(topic, ImmutableList.of(message1, message2))); + Iterator receivedMessages = pubsub().pull(subscription, 2); + pubsub().nackAsync(subscription, ImmutableList.of(receivedMessages.next().getAckId(), + receivedMessages.next().getAckId())).get(); + receivedMessages = pubsub().pull(subscription, 2); + pubsub().ackAsync(subscription, ImmutableList.of(receivedMessages.next().getAckId(), + receivedMessages.next().getAckId())).get(); + assertFalse(pubsub().pull(subscription, 2).hasNext()); + assertTrue(pubsub().deleteSubscription(subscription)); + assertTrue(pubsub().deleteTopic(topic)); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/MessageConsumerImplTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/MessageConsumerImplTest.java new file mode 100644 index 000000000000..c0acf8199ce0 --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/MessageConsumerImplTest.java @@ -0,0 +1,453 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import com.google.cloud.GrpcServiceOptions.ExecutorFactory; +import com.google.cloud.pubsub.PubSub.MessageConsumer; +import com.google.cloud.pubsub.PubSub.MessageProcessor; +import com.google.cloud.pubsub.spi.PubSubRpc; +import com.google.cloud.pubsub.spi.PubSubRpc.PullCallback; +import com.google.cloud.pubsub.spi.PubSubRpc.PullFuture; +import com.google.common.util.concurrent.ForwardingListenableFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.pubsub.v1.PullRequest; +import com.google.pubsub.v1.PullResponse; + +import org.easymock.EasyMock; +import org.easymock.IAnswer; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +public class MessageConsumerImplTest { + + private static final String PROJECT = "project"; + private static final String SUBSCRIPTION = "subscription"; + private static final String SUBSCRIPTION_PB = "projects/project/subscriptions/subscription"; + private static final int MAX_QUEUED_CALLBACKS = 42; + private static final Message MESSAGE1 = Message.of("payload1"); + private static final Message MESSAGE2 = Message.of("payload2"); + private static final String ACK_ID1 = "ack-id1"; + private static final String ACK_ID2 = "ack-id2"; + private static final com.google.pubsub.v1.ReceivedMessage MESSAGE1_PB = + com.google.pubsub.v1.ReceivedMessage.newBuilder() + .setAckId(ACK_ID1) + .setMessage(MESSAGE1.toPb()) + .build(); + private static final com.google.pubsub.v1.ReceivedMessage MESSAGE2_PB = + com.google.pubsub.v1.ReceivedMessage.newBuilder() + .setAckId(ACK_ID2) + .setMessage(MESSAGE2.toPb()) + .build(); + private static final PullResponse PULL_RESPONSE = PullResponse.newBuilder() + .addReceivedMessages(MESSAGE1_PB) + .addReceivedMessages(MESSAGE2_PB) + .build(); + private static final MessageProcessor DO_NOTHING_PROCESSOR = new MessageProcessor() { + @Override + public void process(Message message) throws Exception { + // do nothing + } + }; + private static final MessageProcessor THROW_PROCESSOR = new MessageProcessor() { + @Override + public void process(Message message) throws Exception { + throw new RuntimeException(); + } + }; + private static final PullResponse EMPTY_RESPONSE = PullResponse.getDefaultInstance(); + + private PubSubRpc pubsubRpc; + private PubSub pubsub; + private PubSubOptions options; + private AckDeadlineRenewer renewer; + + @Rule + public Timeout globalTimeout = Timeout.seconds(60); + + static final class TestPullFuture + extends ForwardingListenableFuture.SimpleForwardingListenableFuture + implements PullFuture { + + TestPullFuture(PullResponse response) { + super(Futures.immediateFuture(response)); + } + + @Override + public void addCallback(final PullCallback callback) { + Futures.addCallback(delegate(), new FutureCallback() { + @Override + public void onSuccess(PullResponse result) { + callback.success(result); + } + + @Override + public void onFailure(Throwable error) { + callback.failure(error); + } + }); + } + } + + @Before + public void setUp() { + pubsubRpc = EasyMock.createStrictMock(PubSubRpc.class); + pubsub = EasyMock.createMock(PubSub.class); + options = EasyMock.createStrictMock(PubSubOptions.class); + renewer = EasyMock.createMock(AckDeadlineRenewer.class); + } + + @After + public void tearDown() { + EasyMock.verify(pubsubRpc); + EasyMock.verify(pubsub); + EasyMock.verify(options); + EasyMock.verify(renewer); + + } + + private static PullRequest pullRequest(int maxQueuedCallbacks) { + return PullRequest.newBuilder() + .setMaxMessages(maxQueuedCallbacks) + .setSubscription(SUBSCRIPTION_PB) + .setReturnImmediately(false) + .build(); + } + + private static IAnswer createAnswer(final CountDownLatch latch) { + return new IAnswer() { + @Override + public Void answer() throws Throwable { + latch.countDown(); + return null; + } + }; + } + + @Test + public void testMessageConsumerAck() throws Exception { + PullRequest request = pullRequest(MAX_QUEUED_CALLBACKS); + EasyMock.expect(options.getRpc()).andReturn(pubsubRpc); + EasyMock.expect(options.getService()).andReturn(pubsub); + EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes(); + EasyMock.expect(pubsub.getOptions()).andReturn(options).times(2); + final CountDownLatch latch = new CountDownLatch(2); + EasyMock.expect(pubsub.ackAsync(SUBSCRIPTION, ACK_ID1)).andReturn(null); + EasyMock.expect(pubsub.ackAsync(SUBSCRIPTION, ACK_ID2)).andReturn(null); + EasyMock.replay(pubsub); + EasyMock.expect(pubsubRpc.pull(request)).andReturn(new TestPullFuture(PULL_RESPONSE)); + EasyMock.expect(pubsubRpc.pull(EasyMock.anyObject())) + .andReturn(new TestPullFuture(EMPTY_RESPONSE)).anyTimes(); + renewer.add(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall(); + renewer.add(SUBSCRIPTION, ACK_ID2); + EasyMock.expectLastCall(); + renewer.remove(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + renewer.remove(SUBSCRIPTION, ACK_ID2); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + EasyMock.replay(pubsubRpc, options, renewer); + try (MessageConsumer consumer = + MessageConsumerImpl.builder(options, SUBSCRIPTION, renewer, DO_NOTHING_PROCESSOR) + .maxQueuedCallbacks(MAX_QUEUED_CALLBACKS) + .build()) { + latch.await(); + } + } + + @Test + public void testMessageConsumerNack() throws Exception { + PullRequest request = pullRequest(MAX_QUEUED_CALLBACKS); + EasyMock.expect(options.getRpc()).andReturn(pubsubRpc); + EasyMock.expect(options.getService()).andReturn(pubsub); + EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes(); + EasyMock.expect(pubsub.getOptions()).andReturn(options).times(2); + final CountDownLatch latch = new CountDownLatch(2); + EasyMock.expect(pubsub.nackAsync(SUBSCRIPTION, ACK_ID1)).andReturn(null); + EasyMock.expect(pubsub.nackAsync(SUBSCRIPTION, ACK_ID2)).andReturn(null); + EasyMock.replay(pubsub); + EasyMock.expect(pubsubRpc.pull(request)).andReturn(new TestPullFuture(PULL_RESPONSE)); + EasyMock.expect(pubsubRpc.pull(EasyMock.anyObject())) + .andReturn(new TestPullFuture(EMPTY_RESPONSE)).anyTimes(); + renewer.add(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall(); + renewer.add(SUBSCRIPTION, ACK_ID2); + EasyMock.expectLastCall(); + renewer.remove(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + renewer.remove(SUBSCRIPTION, ACK_ID2); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + EasyMock.replay(pubsubRpc, options, renewer); + try (MessageConsumer consumer = + MessageConsumerImpl.builder(options, SUBSCRIPTION, renewer, THROW_PROCESSOR) + .maxQueuedCallbacks(MAX_QUEUED_CALLBACKS) + .build()) { + latch.await(); + } + } + + @Test + public void testMessageConsumerMultipleCallsAck() throws Exception { + PullRequest request1 = pullRequest(MAX_QUEUED_CALLBACKS); + PullRequest request2 = pullRequest(MAX_QUEUED_CALLBACKS - 1); + PullResponse response1 = PullResponse.newBuilder() + .addReceivedMessages(MESSAGE1_PB) + .build(); + final PullResponse response2 = PullResponse.newBuilder() + .addReceivedMessages(MESSAGE2_PB) + .build(); + EasyMock.expect(options.getRpc()).andReturn(pubsubRpc); + EasyMock.expect(options.getService()).andReturn(pubsub); + EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes(); + final CountDownLatch nextPullLatch = new CountDownLatch(1); + final CountDownLatch latch = new CountDownLatch(2); + EasyMock.expect(pubsub.getOptions()).andReturn(options); + EasyMock.expect(pubsub.ackAsync(SUBSCRIPTION, ACK_ID1)).andAnswer(new IAnswer>() { + @Override + public Future answer() throws Throwable { + nextPullLatch.await(); + return null; + } + }); + EasyMock.expect(pubsub.getOptions()).andReturn(options); + EasyMock.expect(pubsub.ackAsync(SUBSCRIPTION, ACK_ID2)).andReturn(null); + EasyMock.replay(pubsub); + EasyMock.expect(pubsubRpc.pull(request1)).andReturn(new TestPullFuture(response1)); + EasyMock.expect(pubsubRpc.pull(request2)).andAnswer(new IAnswer() { + @Override + public PullFuture answer() throws Throwable { + nextPullLatch.countDown(); + return new TestPullFuture(response2); + } + }); + EasyMock.expect(pubsubRpc.pull(EasyMock.anyObject())) + .andReturn(new TestPullFuture(EMPTY_RESPONSE)).anyTimes(); + renewer.add(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall(); + renewer.remove(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + renewer.add(SUBSCRIPTION, ACK_ID2); + EasyMock.expectLastCall(); + renewer.remove(SUBSCRIPTION, ACK_ID2); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + EasyMock.replay(pubsubRpc, options, renewer); + try (MessageConsumer consumer = + MessageConsumerImpl.builder(options, SUBSCRIPTION, renewer, DO_NOTHING_PROCESSOR) + .maxQueuedCallbacks(MAX_QUEUED_CALLBACKS) + .build()) { + latch.await(); + } + } + + @Test + public void testMessageConsumerMultipleCallsNack() throws Exception { + PullRequest request1 = pullRequest(MAX_QUEUED_CALLBACKS); + PullRequest request2 = pullRequest(MAX_QUEUED_CALLBACKS - 1); + PullResponse response1 = PullResponse.newBuilder() + .addReceivedMessages(MESSAGE1_PB) + .build(); + final PullResponse response2 = PullResponse.newBuilder() + .addReceivedMessages(MESSAGE2_PB) + .build(); + EasyMock.expect(options.getRpc()).andReturn(pubsubRpc); + EasyMock.expect(options.getService()).andReturn(pubsub); + EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes(); + final CountDownLatch nextPullLatch = new CountDownLatch(1); + final CountDownLatch latch = new CountDownLatch(2); + EasyMock.expect(pubsub.getOptions()).andReturn(options); + EasyMock.expect(pubsub.nackAsync(SUBSCRIPTION, ACK_ID1)).andAnswer(new IAnswer>() { + @Override + public Future answer() throws Throwable { + nextPullLatch.await(); + return null; + } + }); + EasyMock.expect(pubsub.getOptions()).andReturn(options); + EasyMock.expect(pubsub.nackAsync(SUBSCRIPTION, ACK_ID2)).andReturn(null); + EasyMock.replay(pubsub); + EasyMock.expect(pubsubRpc.pull(request1)).andReturn(new TestPullFuture(response1)); + EasyMock.expect(pubsubRpc.pull(request2)).andAnswer(new IAnswer() { + @Override + public PullFuture answer() throws Throwable { + nextPullLatch.countDown(); + return new TestPullFuture(response2); + } + }); + EasyMock.expect(pubsubRpc.pull(EasyMock.anyObject())) + .andReturn(new TestPullFuture(EMPTY_RESPONSE)).anyTimes(); + renewer.add(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall(); + renewer.remove(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + renewer.add(SUBSCRIPTION, ACK_ID2); + EasyMock.expectLastCall(); + renewer.remove(SUBSCRIPTION, ACK_ID2); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + EasyMock.replay(pubsubRpc, options, renewer); + try (MessageConsumer consumer = + MessageConsumerImpl.builder(options, SUBSCRIPTION, renewer, THROW_PROCESSOR) + .maxQueuedCallbacks(MAX_QUEUED_CALLBACKS) + .build()) { + latch.await(); + } + } + + @Test + public void testMessageConsumerMaxCallbacksAck() throws Exception { + PullRequest request1 = pullRequest(2); + PullRequest request2 = pullRequest(1); + final PullResponse otherPullResponse = PullResponse.newBuilder() + .addReceivedMessages(MESSAGE1_PB) + .build(); + EasyMock.expect(options.getRpc()).andReturn(pubsubRpc); + EasyMock.expect(options.getService()).andReturn(pubsub); + EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes(); + EasyMock.expect(pubsub.getOptions()).andReturn(options).times(2); + final CountDownLatch nextPullLatch = new CountDownLatch(1); + final CountDownLatch latch = new CountDownLatch(3); + EasyMock.expect(pubsub.ackAsync(SUBSCRIPTION, ACK_ID1)).andReturn(null); + EasyMock.expect(pubsub.ackAsync(SUBSCRIPTION, ACK_ID2)).andAnswer(new IAnswer>() { + @Override + public Future answer() throws Throwable { + nextPullLatch.await(); + return null; + } + }); + EasyMock.expect(pubsub.getOptions()).andReturn(options); + EasyMock.expect(pubsub.ackAsync(SUBSCRIPTION, ACK_ID1)).andReturn(null); + EasyMock.replay(pubsub); + EasyMock.expect(pubsubRpc.pull(request1)).andReturn(new TestPullFuture(PULL_RESPONSE)); + EasyMock.expect(pubsubRpc.pull(request2)).andAnswer(new IAnswer() { + @Override + public PullFuture answer() throws Throwable { + nextPullLatch.countDown(); + return new TestPullFuture(otherPullResponse); + } + }); + EasyMock.expect(pubsubRpc.pull(EasyMock.anyObject())) + .andReturn(new TestPullFuture(EMPTY_RESPONSE)).anyTimes(); + renewer.add(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall(); + renewer.add(SUBSCRIPTION, ACK_ID2); + EasyMock.expectLastCall(); + renewer.remove(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + renewer.remove(SUBSCRIPTION, ACK_ID2); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + renewer.add(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall(); + renewer.remove(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + EasyMock.replay(pubsubRpc, options, renewer); + try (MessageConsumer consumer = + MessageConsumerImpl.builder(options, SUBSCRIPTION, renewer, DO_NOTHING_PROCESSOR) + .maxQueuedCallbacks(2) + .build()) { + latch.await(); + } + } + + @Test + public void testMessageConsumerMaxCallbacksNack() throws Exception { + PullRequest request1 = pullRequest(2); + PullRequest request2 = pullRequest(1); + final PullResponse otherPullResponse = PullResponse.newBuilder() + .addReceivedMessages(MESSAGE1_PB) + .build(); + EasyMock.expect(options.getRpc()).andReturn(pubsubRpc); + EasyMock.expect(options.getService()).andReturn(pubsub); + EasyMock.expect(options.getProjectId()).andReturn(PROJECT).anyTimes(); + EasyMock.expect(pubsub.getOptions()).andReturn(options).times(2); + final CountDownLatch nextPullLatch = new CountDownLatch(1); + final CountDownLatch latch = new CountDownLatch(3); + EasyMock.expect(pubsub.nackAsync(SUBSCRIPTION, ACK_ID1)).andReturn(null); + EasyMock.expect(pubsub.nackAsync(SUBSCRIPTION, ACK_ID2)).andAnswer(new IAnswer>() { + @Override + public Future answer() throws Throwable { + nextPullLatch.await(); + return null; + } + }); + EasyMock.expect(pubsub.getOptions()).andReturn(options); + EasyMock.expect(pubsub.nackAsync(SUBSCRIPTION, ACK_ID1)).andReturn(null); + EasyMock.replay(pubsub); + EasyMock.expect(pubsubRpc.pull(request1)).andReturn(new TestPullFuture(PULL_RESPONSE)); + EasyMock.expect(pubsubRpc.pull(request2)).andAnswer(new IAnswer() { + @Override + public PullFuture answer() throws Throwable { + nextPullLatch.countDown(); + return new TestPullFuture(otherPullResponse); + } + }); + EasyMock.expect(pubsubRpc.pull(EasyMock.anyObject())) + .andReturn(new TestPullFuture(EMPTY_RESPONSE)).anyTimes(); + renewer.add(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall(); + renewer.add(SUBSCRIPTION, ACK_ID2); + EasyMock.expectLastCall(); + renewer.remove(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + renewer.remove(SUBSCRIPTION, ACK_ID2); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + renewer.add(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall(); + renewer.remove(SUBSCRIPTION, ACK_ID1); + EasyMock.expectLastCall().andAnswer(createAnswer(latch)); + EasyMock.replay(pubsubRpc, options, renewer); + try (MessageConsumer consumer = + MessageConsumerImpl.builder(options, SUBSCRIPTION, renewer, THROW_PROCESSOR) + .maxQueuedCallbacks(2) + .build()) { + latch.await(); + } + } + + @Test + public void testClose() throws Exception { + EasyMock.expect(options.getRpc()).andReturn(pubsubRpc); + EasyMock.expect(options.getService()).andReturn(pubsub); + final ExecutorService executor = EasyMock.createStrictMock(ExecutorService.class); + executor.shutdown(); + EasyMock.expectLastCall(); + EasyMock.replay(pubsubRpc, pubsub, options, executor, renewer); + MessageConsumer consumer = + MessageConsumerImpl.builder(options, SUBSCRIPTION, renewer, DO_NOTHING_PROCESSOR) + .maxQueuedCallbacks(MAX_QUEUED_CALLBACKS) + .executorFactory(new ExecutorFactory() { + @Override + public ExecutorService get() { + return executor; + } + + @Override + public void release(ExecutorService executor) { + executor.shutdown(); + } + }).build(); + consumer.close(); + // closing again should do nothing + consumer.close(); + EasyMock.verify(executor); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/MessageTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/MessageTest.java new file mode 100644 index 000000000000..ee4fe3055a10 --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/MessageTest.java @@ -0,0 +1,176 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import com.google.cloud.ByteArray; +import com.google.common.collect.ImmutableMap; + +import org.junit.Test; + +import java.nio.charset.StandardCharsets; +import java.util.Map; + +public class MessageTest { + + private static final String MESSAGE_ID = "messageId"; + private static final String PAYLOAD_STRING = "payload"; + private static final ByteArray PAYLOAD = + ByteArray.copyFrom("payload".getBytes(StandardCharsets.UTF_8)); + private static final Map ATTRIBUTES = + ImmutableMap.of("key1", "value1", "key2", "value2"); + private static final Long PUBLISH_TIME = 42L; + private static final Message MESSAGE_STRING = Message.newBuilder(PAYLOAD_STRING) + .setId(MESSAGE_ID) + .setAttributes(ATTRIBUTES) + .setPublishTime(PUBLISH_TIME) + .build(); + private static final Message MESSAGE = Message.newBuilder(PAYLOAD) + .setId(MESSAGE_ID) + .setAttributes(ATTRIBUTES) + .setPublishTime(PUBLISH_TIME) + .build(); + private static final Message DEPRECATED_MESSAGE_STRING = Message.builder(PAYLOAD_STRING) + .setId(MESSAGE_ID) + .attributes(ATTRIBUTES) + .setPublishTime(PUBLISH_TIME) + .build(); + private static final Message DEPRECATED_MESSAGE = Message.builder(PAYLOAD) + .setId(MESSAGE_ID) + .attributes(ATTRIBUTES) + .setPublishTime(PUBLISH_TIME) + .build(); + + @Test + public void testToBuilder() { + compareMessage(MESSAGE, MESSAGE.toBuilder().build()); + Message message = MESSAGE.toBuilder() + .setPayload("newPayload") + .clearAttributes() + .addAttribute("key1", "value1") + .build(); + assertEquals("newPayload", message.getPayloadAsString()); + assertEquals(ImmutableMap.of("key1", "value1"), message.getAttributes()); + message = message.toBuilder() + .setPayload(PAYLOAD_STRING) + .removeAttribute("key1") + .setAttributes(ATTRIBUTES) + .build(); + compareMessage(MESSAGE, message); + } + + @Test + public void testBuilder() { + assertEquals(MESSAGE_ID, MESSAGE.getId()); + assertEquals(PAYLOAD, MESSAGE.getPayload()); + assertEquals(PAYLOAD_STRING, MESSAGE.getPayloadAsString()); + assertEquals(ATTRIBUTES, MESSAGE.getAttributes()); + assertEquals(PUBLISH_TIME, MESSAGE.getPublishTime()); + assertEquals(MESSAGE_ID, MESSAGE_STRING.getId()); + assertEquals(PAYLOAD, MESSAGE_STRING.getPayload()); + assertEquals(PAYLOAD_STRING, MESSAGE_STRING.getPayloadAsString()); + assertEquals(ATTRIBUTES, MESSAGE_STRING.getAttributes()); + assertEquals(PUBLISH_TIME, MESSAGE_STRING.getPublishTime()); + compareMessage(MESSAGE, MESSAGE_STRING); + Message message = Message.newBuilder(PAYLOAD) + .setId(MESSAGE_ID) + .setAttributes(ATTRIBUTES) + .clearAttributes() + .addAttribute("key1", "value1") + .addAttribute("key2", "value2") + .setPublishTime(PUBLISH_TIME) + .build(); + assertEquals(MESSAGE_ID, message.getId()); + assertEquals(PAYLOAD, message.getPayload()); + assertEquals(PAYLOAD_STRING, message.getPayloadAsString()); + assertEquals(ATTRIBUTES, message.getAttributes()); + assertEquals(PUBLISH_TIME, message.getPublishTime()); + compareMessage(MESSAGE, message); + } + + @Test + public void testBuilderDeprecated() { + assertEquals(MESSAGE_ID, DEPRECATED_MESSAGE.id()); + assertEquals(PAYLOAD, DEPRECATED_MESSAGE.payload()); + assertEquals(PAYLOAD_STRING, DEPRECATED_MESSAGE.payloadAsString()); + assertEquals(ATTRIBUTES, DEPRECATED_MESSAGE.attributes()); + assertEquals(PUBLISH_TIME, DEPRECATED_MESSAGE.publishTime()); + assertEquals(MESSAGE_ID, DEPRECATED_MESSAGE_STRING.id()); + assertEquals(PAYLOAD, DEPRECATED_MESSAGE_STRING.payload()); + assertEquals(PAYLOAD_STRING, DEPRECATED_MESSAGE_STRING.payloadAsString()); + assertEquals(ATTRIBUTES, DEPRECATED_MESSAGE_STRING.attributes()); + assertEquals(PUBLISH_TIME, DEPRECATED_MESSAGE_STRING.publishTime()); + compareMessage(MESSAGE, DEPRECATED_MESSAGE_STRING); + Message message = Message.builder(PAYLOAD) + .setId(MESSAGE_ID) + .attributes(ATTRIBUTES) + .clearAttributes() + .addAttribute("key1", "value1") + .addAttribute("key2", "value2") + .setPublishTime(PUBLISH_TIME) + .build(); + assertEquals(MESSAGE_ID, message.id()); + assertEquals(PAYLOAD, message.payload()); + assertEquals(PAYLOAD_STRING, message.payloadAsString()); + assertEquals(ATTRIBUTES, message.attributes()); + assertEquals(PUBLISH_TIME, message.publishTime()); + compareMessage(MESSAGE, message); + } + + @Test + public void testOf() { + Message message1 = Message.of(PAYLOAD_STRING); + assertNull(message1.getId()); + assertEquals(PAYLOAD, message1.getPayload()); + assertEquals(PAYLOAD_STRING, message1.getPayloadAsString()); + assertEquals(ImmutableMap.of(), message1.getAttributes()); + assertNull(message1.getPublishTime()); + Message message2 = Message.of(PAYLOAD); + assertNull(message2.getId()); + assertEquals(PAYLOAD, message2.getPayload()); + assertEquals(PAYLOAD_STRING, message2.getPayloadAsString()); + assertEquals(ImmutableMap.of(), message2.getAttributes()); + assertNull(message2.getPublishTime()); + compareMessage(message1 ,message2); + } + + @Test + public void testToAndFromPb() { + compareMessage(MESSAGE, Message.fromPb(MESSAGE.toPb())); + compareMessage(MESSAGE_STRING, Message.fromPb(MESSAGE_STRING.toPb())); + } + + @Test + public void testToAndFromPbIncomplete() { + Message message = Message.of(PAYLOAD_STRING); + compareMessage(message, Message.fromPb(message.toPb())); + message = Message.of(PAYLOAD); + compareMessage(message, Message.fromPb(message.toPb())); + } + + private void compareMessage(Message expected, Message value) { + assertEquals(expected, value); + assertEquals(expected.getId(), value.getId()); + assertEquals(expected.getPayload(), value.getPayload()); + assertEquals(expected.getPayloadAsString(), value.getPayloadAsString()); + assertEquals(expected.getAttributes(), value.getAttributes()); + assertEquals(expected.getPublishTime(), value.getPublishTime()); + assertEquals(expected.hashCode(), value.hashCode()); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/OptionTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/OptionTest.java new file mode 100644 index 000000000000..8e4230198d2a --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/OptionTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; + +import com.google.cloud.pubsub.Option.OptionType; +import com.google.cloud.pubsub.PubSub.ListOption; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class OptionTest { + + private static final OptionType OPTION_TYPE = ListOption.OptionType.PAGE_SIZE; + private static final OptionType ANOTHER_OPTION_TYPE = ListOption.OptionType.PAGE_TOKEN; + private static final String VALUE = "some value"; + private static final String OTHER_VALUE = "another value"; + private static final Option OPTION = new Option(OPTION_TYPE, VALUE) {}; + private static final Option OPTION_EQUALS = new Option(OPTION_TYPE, VALUE) {}; + private static final Option OPTION_NOT_EQUALS1 = new Option(ANOTHER_OPTION_TYPE, OTHER_VALUE) {}; + private static final Option OPTION_NOT_EQUALS2 = new Option(ANOTHER_OPTION_TYPE, VALUE) {}; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testEquals() { + assertEquals(OPTION, OPTION_EQUALS); + assertNotEquals(OPTION, OPTION_NOT_EQUALS1); + assertNotEquals(OPTION, OPTION_NOT_EQUALS2); + } + + @Test + public void testHashCode() { + assertEquals(OPTION.hashCode(), OPTION_EQUALS.hashCode()); + } + + @Test + public void testConstructor() { + assertEquals(OPTION_TYPE, OPTION.getOptionType()); + assertEquals(VALUE, OPTION.getValue()); + Option option = new Option(OPTION_TYPE, null) {}; + assertEquals(OPTION_TYPE, option.getOptionType()); + assertNull(option.getValue()); + thrown.expect(NullPointerException.class); + new Option(null, VALUE) {}; + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/PubSubTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/PubSubTest.java new file mode 100644 index 000000000000..78322f4eed95 --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/PubSubTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +import com.google.cloud.GrpcServiceOptions.ExecutorFactory; +import com.google.cloud.pubsub.PubSub.ListOption; +import com.google.cloud.pubsub.PubSub.PullOption; + +import org.easymock.EasyMock; +import org.junit.Test; + +public class PubSubTest { + + private static final int PAGE_SIZE = 42; + private static final String PAGE_TOKEN = "page token"; + private static final int MAX_QUEUED_CALLBACKS = 42; + + @Test + public void testListOption() { + // page token + ListOption listOption = ListOption.pageToken(PAGE_TOKEN); + assertEquals(PAGE_TOKEN, listOption.getValue()); + assertEquals(ListOption.OptionType.PAGE_TOKEN, listOption.getOptionType()); + // page size + listOption = ListOption.pageSize(PAGE_SIZE); + assertEquals(PAGE_SIZE, listOption.getValue()); + assertEquals(ListOption.OptionType.PAGE_SIZE, listOption.getOptionType()); + } + + @Test + @SuppressWarnings("unchecked") + public void testPullOptions() { + // max queued callbacks + PullOption pullOption = PullOption.maxQueuedCallbacks(MAX_QUEUED_CALLBACKS); + assertEquals(MAX_QUEUED_CALLBACKS, pullOption.getValue()); + assertEquals(PullOption.OptionType.MAX_QUEUED_CALLBACKS, pullOption.getOptionType()); + ExecutorFactory executorFactory = EasyMock.createStrictMock(ExecutorFactory.class); + pullOption = PullOption.executorFactory(executorFactory); + assertSame(executorFactory, pullOption.getValue()); + assertEquals(PullOption.OptionType.EXECUTOR_FACTORY, pullOption.getOptionType()); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/PushConfigTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/PushConfigTest.java new file mode 100644 index 000000000000..baf8ea4d3535 --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/PushConfigTest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static org.junit.Assert.assertEquals; + +import com.google.common.collect.ImmutableMap; + +import org.junit.Test; + +import java.util.Map; + +public class PushConfigTest { + + private static final String ENDPOINT = "https://example.com/push"; + private static final Map ATTRIBUTES = + ImmutableMap.of("key1", "value1", "key2", "value2"); + private static final PushConfig PUSH_CONFIG = PushConfig.newBuilder(ENDPOINT, ATTRIBUTES).build(); + private static final PushConfig DEPRECATED_PUSH_CONFIG = + PushConfig.builder(ENDPOINT, ATTRIBUTES).build(); + + @Test + public void testToBuilder() { + comparePushConfig(PUSH_CONFIG, PUSH_CONFIG.toBuilder().build()); + PushConfig pushConfig = PUSH_CONFIG.toBuilder() + .setEndpoint("https://example2.com/push") + .clearAttributes() + .addAttribute("key1", "value1") + .build(); + assertEquals("https://example2.com/push", pushConfig.getEndpoint()); + assertEquals(ImmutableMap.of("key1", "value1"), pushConfig.getAttributes()); + pushConfig = pushConfig.toBuilder() + .setEndpoint(ENDPOINT) + .removeAttribute("key1") + .setAttributes(ATTRIBUTES) + .build(); + comparePushConfig(PUSH_CONFIG, pushConfig); + } + + @Test + public void testBuilder() { + assertEquals(ENDPOINT, DEPRECATED_PUSH_CONFIG.endpoint()); + assertEquals(ATTRIBUTES, DEPRECATED_PUSH_CONFIG.attributes()); + PushConfig pushConfig = PushConfig.builder("https://example2.com/push") + .endpoint(ENDPOINT) + .attributes(ATTRIBUTES) + .clearAttributes() + .addAttribute("key1", "value1") + .addAttribute("key2", "value2") + .build(); + assertEquals(ENDPOINT, pushConfig.endpoint()); + assertEquals(ATTRIBUTES, pushConfig.attributes()); + comparePushConfig(PUSH_CONFIG, pushConfig); + } + + @Test + public void testBuilderDeprecated() { + assertEquals(ENDPOINT, PUSH_CONFIG.getEndpoint()); + assertEquals(ATTRIBUTES, PUSH_CONFIG.getAttributes()); + PushConfig pushConfig = PushConfig.newBuilder("https://example2.com/push") + .setEndpoint(ENDPOINT) + .setAttributes(ATTRIBUTES) + .clearAttributes() + .addAttribute("key1", "value1") + .addAttribute("key2", "value2") + .build(); + assertEquals(ENDPOINT, pushConfig.getEndpoint()); + assertEquals(ATTRIBUTES, pushConfig.getAttributes()); + comparePushConfig(PUSH_CONFIG, pushConfig); + } + + @Test + public void testOf() { + PushConfig pushConfig = PushConfig.of(ENDPOINT); + assertEquals(ENDPOINT, pushConfig.getEndpoint()); + assertEquals(ImmutableMap.of(), pushConfig.getAttributes()); + pushConfig = PushConfig.of(ENDPOINT, ATTRIBUTES); + assertEquals(ENDPOINT, pushConfig.getEndpoint()); + assertEquals(ATTRIBUTES, pushConfig.getAttributes()); + comparePushConfig(PUSH_CONFIG, pushConfig); + } + + @Test + public void testToAndFromPb() { + comparePushConfig(PUSH_CONFIG, PushConfig.fromPb(PUSH_CONFIG.toPb())); + } + + @Test + public void testToAndFromPbIncomplete() { + PushConfig pushConfig = PushConfig.of(ENDPOINT); + comparePushConfig(pushConfig, PushConfig.fromPb(pushConfig.toPb())); + } + + private void comparePushConfig(PushConfig expected, PushConfig value) { + assertEquals(expected, value); + assertEquals(expected.getEndpoint(), value.getEndpoint()); + assertEquals(expected.getAttributes(), value.getAttributes()); + assertEquals(expected.hashCode(), value.hashCode()); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/ReceivedMessageTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/ReceivedMessageTest.java new file mode 100644 index 000000000000..c740063c533b --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/ReceivedMessageTest.java @@ -0,0 +1,223 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import com.google.api.client.util.Charsets; +import com.google.cloud.ByteArray; +import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.Futures; + +import org.easymock.EasyMock; +import org.junit.After; +import org.junit.Test; + +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +public class ReceivedMessageTest { + + private static final String SUBSCRIPTION = "subscription"; + private static final String ACK_ID = "ackId"; + private static final String MESSAGE_ID = "messageId"; + private static final String PAYLOAD_STRING = "payload"; + private static final ByteArray PAYLOAD = + ByteArray.copyFrom("payload".getBytes(StandardCharsets.UTF_8)); + private static final Map ATTRIBUTES = + ImmutableMap.of("key1", "value1", "key2", "value2"); + private static final Long PUBLISH_TIME = 42L; + private static final Message MESSAGE = Message.newBuilder(PAYLOAD) + .setId(MESSAGE_ID) + .setAttributes(ATTRIBUTES) + .setPublishTime(PUBLISH_TIME) + .build(); + private static final com.google.pubsub.v1.ReceivedMessage RECEIVED_MESSAGE_PB = + com.google.pubsub.v1.ReceivedMessage.newBuilder() + .setMessage(MESSAGE.toPb()) + .setAckId(ACK_ID) + .build(); + + private final PubSub serviceMockReturnsOptions = createStrictMock(PubSub.class); + private final PubSubOptions mockOptions = createMock(PubSubOptions.class); + private PubSub pubsub; + private ReceivedMessage expectedMessage; + private ReceivedMessage message; + + private void initializeExpectedMessage(int optionsCalls) { + expect(serviceMockReturnsOptions.getOptions()).andReturn(mockOptions).times(optionsCalls); + replay(serviceMockReturnsOptions); + pubsub = createStrictMock(PubSub.class); + expectedMessage = + ReceivedMessage.fromPb(serviceMockReturnsOptions, SUBSCRIPTION, RECEIVED_MESSAGE_PB); + } + + private void initializeMessage() { + message = ReceivedMessage.fromPb(pubsub, SUBSCRIPTION, RECEIVED_MESSAGE_PB); + } + + @After + public void tearDown() throws Exception { + verify(pubsub, serviceMockReturnsOptions); + } + + @Test + public void testBuilder() { + initializeExpectedMessage(3); + replay(pubsub); + Map attributes = ImmutableMap.of("newKey1", "newVal1"); + ReceivedMessage builtMessage = expectedMessage.toBuilder() + .setPayload("newPayload") + .setId("newMessageId") + .setAttributes(attributes) + .setPublishTime(PUBLISH_TIME + 1) + .build(); + assertSame(serviceMockReturnsOptions, builtMessage.getPubsub()); + assertEquals(SUBSCRIPTION, builtMessage.getSubscription()); + assertEquals(ACK_ID, builtMessage.getAckId()); + assertEquals("newMessageId", builtMessage.getId()); + assertArrayEquals("newPayload".getBytes(Charsets.UTF_8), builtMessage.getPayload().toByteArray()); + assertEquals("newPayload", builtMessage.getPayloadAsString()); + assertEquals(attributes, builtMessage.getAttributes()); + assertEquals(PUBLISH_TIME + 1, (long) builtMessage.getPublishTime()); + builtMessage = builtMessage.toBuilder() + .setPayload(PAYLOAD) + .setId(MESSAGE_ID) + .clearAttributes() + .addAttribute("key1", "value1") + .addAttribute("key2", "value2") + .setPublishTime(PUBLISH_TIME) + .build(); + assertSame(serviceMockReturnsOptions, builtMessage.getPubsub()); + assertEquals(MESSAGE_ID, builtMessage.getId()); + assertEquals(PAYLOAD, builtMessage.getPayload()); + assertEquals(PAYLOAD_STRING, builtMessage.getPayloadAsString()); + assertEquals(ATTRIBUTES, builtMessage.getAttributes()); + assertEquals(PUBLISH_TIME, builtMessage.getPublishTime()); + compareReceivedMessage(expectedMessage, builtMessage); + } + + @Test + public void testBuilderDeprecated() { + initializeExpectedMessage(3); + replay(pubsub); + Map attributes = ImmutableMap.of("newKey1", "newVal1"); + ReceivedMessage builtMessage = expectedMessage.toBuilder() + .payload("newPayload") + .setId("newMessageId") + .attributes(attributes) + .setPublishTime(PUBLISH_TIME + 1) + .build(); + assertSame(serviceMockReturnsOptions, builtMessage.pubsub()); + assertEquals(SUBSCRIPTION, builtMessage.subscription()); + assertEquals(ACK_ID, builtMessage.ackId()); + assertEquals("newMessageId", builtMessage.id()); + assertArrayEquals("newPayload".getBytes(Charsets.UTF_8), builtMessage.payload().toByteArray()); + assertEquals("newPayload", builtMessage.payloadAsString()); + assertEquals(attributes, builtMessage.attributes()); + assertEquals(PUBLISH_TIME + 1, (long) builtMessage.publishTime()); + builtMessage = builtMessage.toBuilder() + .payload(PAYLOAD) + .setId(MESSAGE_ID) + .clearAttributes() + .addAttribute("key1", "value1") + .addAttribute("key2", "value2") + .setPublishTime(PUBLISH_TIME) + .build(); + assertSame(serviceMockReturnsOptions, builtMessage.pubsub()); + assertEquals(MESSAGE_ID, builtMessage.id()); + assertEquals(PAYLOAD, builtMessage.payload()); + assertEquals(PAYLOAD_STRING, builtMessage.payloadAsString()); + assertEquals(ATTRIBUTES, builtMessage.attributes()); + assertEquals(PUBLISH_TIME, builtMessage.publishTime()); + compareReceivedMessage(expectedMessage, builtMessage); + } + + @Test + public void testToBuilder() { + initializeExpectedMessage(2); + replay(pubsub); + compareReceivedMessage(expectedMessage, expectedMessage.toBuilder().build()); + } + + @Test + public void testAck() { + initializeExpectedMessage(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + pubsub.ack(SUBSCRIPTION, ACK_ID); + EasyMock.expectLastCall(); + replay(pubsub); + initializeMessage(); + message.ack(); + } + + @Test + public void testAckAsync() throws ExecutionException, InterruptedException { + initializeExpectedMessage(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.ackAsync(SUBSCRIPTION, ACK_ID)).andReturn(Futures.immediateFuture(null)); + EasyMock.expectLastCall(); + replay(pubsub); + initializeMessage(); + assertNull(message.ackAsync().get()); + } + + @Test + public void testModifyAckDeadline() { + initializeExpectedMessage(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + pubsub.modifyAckDeadline(SUBSCRIPTION, 10, TimeUnit.SECONDS, ACK_ID); + EasyMock.expectLastCall(); + replay(pubsub); + initializeMessage(); + message.modifyAckDeadline(10, TimeUnit.SECONDS); + } + + @Test + public void testModifyAckDeadlineAsync() throws ExecutionException, InterruptedException { + initializeExpectedMessage(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION, 10, TimeUnit.SECONDS, ACK_ID)) + .andReturn(Futures.immediateFuture(null)); + EasyMock.expectLastCall(); + replay(pubsub); + initializeMessage(); + assertNull(message.modifyAckDeadlineAsync(10, TimeUnit.SECONDS).get()); + } + + private void compareReceivedMessage(ReceivedMessage expected, ReceivedMessage value) { + assertEquals(expected, value); + assertEquals(expected.getId(), value.getId()); + assertEquals(expected.getPayload(), value.getPayload()); + assertEquals(expected.getPayloadAsString(), value.getPayloadAsString()); + assertEquals(expected.getAttributes(), value.getAttributes()); + assertEquals(expected.getPublishTime(), value.getPublishTime()); + assertEquals(expected.getAckId(), value.getAckId()); + assertEquals(expected.getSubscription(), value.getSubscription()); + assertEquals(expected.hashCode(), value.hashCode()); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SerializationTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SerializationTest.java new file mode 100644 index 000000000000..b200b42989b1 --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SerializationTest.java @@ -0,0 +1,102 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import com.google.cloud.BaseSerializationTest; +import com.google.cloud.GrpcServiceOptions.ExecutorFactory; +import com.google.cloud.NoCredentials; +import com.google.cloud.Restorable; +import com.google.cloud.pubsub.PubSub.ListOption; +import com.google.cloud.pubsub.PubSub.PullOption; + +import java.io.Serializable; +import java.util.concurrent.ScheduledExecutorService; + +public class SerializationTest extends BaseSerializationTest { + + private static final PubSub PUB_SUB = PubSubOptions.newBuilder() + .setProjectId("p") + .setCredentials(NoCredentials.getInstance()) + .setHost("localhost") + .build().getService(); + private static final Message MESSAGE = Message.of("payload"); + private static final com.google.pubsub.v1.ReceivedMessage RECEIVED_MESSAGE_PB = + com.google.pubsub.v1.ReceivedMessage.newBuilder() + .setMessage(MESSAGE.toPb()) + .setAckId("ackId") + .build(); + private static final ReceivedMessage RECEIVED_MESSAGE = + ReceivedMessage.fromPb(PUB_SUB, "subscription", RECEIVED_MESSAGE_PB); + private static final SubscriptionInfo SUBSCRIPTION_INFO = SubscriptionInfo.of("topic", "sub"); + private static final Subscription SUBSCRIPTION = + new Subscription(PUB_SUB, new SubscriptionInfo.BuilderImpl(SUBSCRIPTION_INFO)); + private static final SubscriptionId SUBSCRIPTION_ID = new SubscriptionId("project", "sub"); + private static final TopicInfo TOPIC_INFO = TopicInfo.of("topic"); + private static final Topic TOPIC = + new Topic(PUB_SUB, new TopicInfo.BuilderImpl(TOPIC_INFO)); + private static final ListOption PAGE_TOKEN_OPTION = ListOption.pageToken("cursor"); + private static final ListOption PAGE_SIZE_OPTION = ListOption.pageSize(42); + private static final PullOption MAX_QUEUED_CALLBACKS_OPTION = PullOption.maxQueuedCallbacks(42); + private static final PullOption EXECUTOR_FACTORY_OPTION = + PullOption.executorFactory(new TestExecutorFactory()); + + public static class TestExecutorFactory + implements ExecutorFactory, Serializable { + + private static final long serialVersionUID = -2154875338174302704L; + + @Override + public ScheduledExecutorService get() { + return null; + } + + @Override + public void release(ScheduledExecutorService executor) { + // do nothing + } + + @Override + public boolean equals(Object obj) { + return obj instanceof TestExecutorFactory; + } + + @Override + public int hashCode() { + return 1; + } + } + + @Override + protected Serializable[] serializableObjects() { + PubSubOptions options = PubSubOptions.newBuilder() + .setProjectId("p1") + .setInitialTimeout(1234) + .build(); + PubSubOptions otherOptions = options.toBuilder() + .setProjectId("p2") + .setExecutorFactory(new TestExecutorFactory()) + .build(); + return new Serializable[]{options, otherOptions, MESSAGE, RECEIVED_MESSAGE, SUBSCRIPTION_INFO, + SUBSCRIPTION, SUBSCRIPTION_ID, TOPIC_INFO, TOPIC, PAGE_TOKEN_OPTION, PAGE_SIZE_OPTION, + MAX_QUEUED_CALLBACKS_OPTION, EXECUTOR_FACTORY_OPTION}; + } + + @Override + protected Restorable[] restorableObjects() { + return null; + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SubscriptionIdTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SubscriptionIdTest.java new file mode 100644 index 000000000000..578794663b74 --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SubscriptionIdTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class SubscriptionIdTest { + + private static final String PROJECT = "project"; + private static final String NAME = "subscription"; + private static final String TOPIC_PB = "projects/project/subscriptions/subscription"; + private static final SubscriptionId SUBSCRIPTION_ID = new SubscriptionId(PROJECT, NAME); + + @Test + public void testConstructor() { + assertEquals(PROJECT, SUBSCRIPTION_ID.getProject()); + assertEquals(NAME, SUBSCRIPTION_ID.getSubscription()); + } + + @Test + public void testConstructorDeprecated() { + assertEquals(PROJECT, SUBSCRIPTION_ID.project()); + assertEquals(NAME, SUBSCRIPTION_ID.subscription()); + } + + @Test + public void testToAndFromPb() { + SubscriptionId subscriptionId = SubscriptionId.fromPb(TOPIC_PB); + compareSubscriptionId(SUBSCRIPTION_ID, subscriptionId); + assertEquals(PROJECT, subscriptionId.getProject()); + assertEquals(NAME, subscriptionId.getSubscription()); + } + + private void compareSubscriptionId(SubscriptionId expected, SubscriptionId value) { + assertEquals(expected, value); + assertEquals(expected.getProject(), value.getProject()); + assertEquals(expected.getSubscription(), value.getSubscription()); + assertEquals(expected.hashCode(), value.hashCode()); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SubscriptionInfoTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SubscriptionInfoTest.java new file mode 100644 index 000000000000..d17f29e78dbf --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SubscriptionInfoTest.java @@ -0,0 +1,162 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Test; + +public class SubscriptionInfoTest { + + private static final TopicId TOPIC = TopicId.of("project", "topic"); + private static final String NAME = "subscription"; + private static final String ENDPOINT = "https://example.com/push"; + private static final PushConfig PUSH_CONFIG = PushConfig.of(ENDPOINT); + private static final int ACK_DEADLINE = 42; + private static final SubscriptionInfo SUBSCRIPTION_INFO = SubscriptionInfo.newBuilder(TOPIC, NAME) + .setPushConfig(PUSH_CONFIG) + .setAckDeadLineSeconds(ACK_DEADLINE) + .build(); + private static final SubscriptionInfo DEPRECATED_SUBSCRIPTION_INFO = + SubscriptionInfo.builder(TOPIC, NAME) + .pushConfig(PUSH_CONFIG) + .ackDeadLineSeconds(ACK_DEADLINE) + .build(); + + @Test + public void testToBuilder() { + compareSubscriptionInfo(SUBSCRIPTION_INFO, SUBSCRIPTION_INFO.toBuilder().build()); + SubscriptionInfo subscriptionInfo = SUBSCRIPTION_INFO.toBuilder() + .setTopic("newTopic") + .setName("newSubscription") + .build(); + assertEquals(TopicId.of("newTopic"), subscriptionInfo.getTopic()); + assertEquals("newSubscription", subscriptionInfo.getName()); + subscriptionInfo = subscriptionInfo.toBuilder().setName(NAME).setTopic(TOPIC).build(); + compareSubscriptionInfo(SUBSCRIPTION_INFO, subscriptionInfo); + } + + @Test + public void testBuilder() { + assertEquals(TOPIC, SUBSCRIPTION_INFO.getTopic()); + assertEquals(NAME, SUBSCRIPTION_INFO.getName()); + assertEquals(PUSH_CONFIG, SUBSCRIPTION_INFO.getPushConfig()); + assertEquals(ACK_DEADLINE, SUBSCRIPTION_INFO.getAckDeadlineSeconds()); + SubscriptionInfo subscriptionInfo = + SubscriptionInfo.newBuilder("topic", "subscription").build(); + assertEquals(TopicId.of("topic"), subscriptionInfo.getTopic()); + assertEquals(NAME, subscriptionInfo.getName()); + assertNull(subscriptionInfo.getPushConfig()); + assertEquals(0, subscriptionInfo.getAckDeadlineSeconds()); + subscriptionInfo = SubscriptionInfo.newBuilder("topic", "subscription") + .setTopic("project", "topic").build(); + assertEquals(TOPIC, subscriptionInfo.getTopic()); + assertEquals(NAME, subscriptionInfo.getName()); + assertNull(subscriptionInfo.getPushConfig()); + assertEquals(0, subscriptionInfo.getAckDeadlineSeconds()); + subscriptionInfo = SubscriptionInfo.newBuilder("topic", "subscription") + .setTopic(TOPIC).build(); + assertEquals(TOPIC, subscriptionInfo.getTopic()); + assertEquals(NAME, subscriptionInfo.getName()); + assertNull(subscriptionInfo.getPushConfig()); + assertEquals(0, subscriptionInfo.getAckDeadlineSeconds()); + } + + @Test + public void testBuilderDeprecated() { + assertEquals(TOPIC, DEPRECATED_SUBSCRIPTION_INFO.topic()); + assertEquals(NAME, DEPRECATED_SUBSCRIPTION_INFO.name()); + assertEquals(PUSH_CONFIG, DEPRECATED_SUBSCRIPTION_INFO.pushConfig()); + assertEquals(ACK_DEADLINE, DEPRECATED_SUBSCRIPTION_INFO.ackDeadlineSeconds()); + SubscriptionInfo subscriptionInfo = SubscriptionInfo.builder("topic", "subscription").build(); + assertEquals(TopicId.of("topic"), subscriptionInfo.topic()); + assertEquals(NAME, subscriptionInfo.name()); + assertNull(subscriptionInfo.pushConfig()); + assertEquals(0, subscriptionInfo.ackDeadlineSeconds()); + subscriptionInfo = SubscriptionInfo.builder("topic", "subscription") + .topic("project", "topic").build(); + assertEquals(TOPIC, subscriptionInfo.topic()); + assertEquals(NAME, subscriptionInfo.name()); + assertNull(subscriptionInfo.pushConfig()); + assertEquals(0, subscriptionInfo.ackDeadlineSeconds()); + subscriptionInfo = SubscriptionInfo.builder("topic", "subscription") + .topic(TOPIC).build(); + assertEquals(TOPIC, subscriptionInfo.topic()); + assertEquals(NAME, subscriptionInfo.name()); + assertNull(subscriptionInfo.pushConfig()); + assertEquals(0, subscriptionInfo.ackDeadlineSeconds()); + } + + @Test + public void testOf() { + SubscriptionInfo subscriptionInfo = SubscriptionInfo.of(TOPIC, NAME); + assertEquals(TOPIC, subscriptionInfo.getTopic()); + assertEquals(NAME, subscriptionInfo.getName()); + assertNull(subscriptionInfo.getPushConfig()); + assertEquals(0, subscriptionInfo.getAckDeadlineSeconds()); + subscriptionInfo = SubscriptionInfo.of("topic", NAME); + assertEquals(TopicId.of("topic"), subscriptionInfo.getTopic()); + assertEquals(NAME, subscriptionInfo.getName()); + assertNull(subscriptionInfo.getPushConfig()); + assertEquals(0, subscriptionInfo.getAckDeadlineSeconds()); + subscriptionInfo = SubscriptionInfo.of(TOPIC, NAME, ENDPOINT); + assertEquals(TOPIC, subscriptionInfo.getTopic()); + assertEquals(NAME, subscriptionInfo.getName()); + assertEquals(PushConfig.of(ENDPOINT), subscriptionInfo.getPushConfig()); + assertEquals(0, subscriptionInfo.getAckDeadlineSeconds()); + subscriptionInfo = SubscriptionInfo.of("topic", NAME, ENDPOINT); + assertEquals(TopicId.of("topic"), subscriptionInfo.getTopic()); + assertEquals(NAME, subscriptionInfo.getName()); + assertEquals(PushConfig.of(ENDPOINT), subscriptionInfo.getPushConfig()); + assertEquals(0, subscriptionInfo.getAckDeadlineSeconds()); + } + + @Test + public void testToAndFromPb() { + compareSubscriptionInfo(SUBSCRIPTION_INFO, + SubscriptionInfo.fromPb(SUBSCRIPTION_INFO.toPb("project"))); + SubscriptionInfo subscriptionInfo = SubscriptionInfo.of(TOPIC, NAME); + compareSubscriptionInfo(subscriptionInfo, + SubscriptionInfo.fromPb(subscriptionInfo.toPb("project"))); + subscriptionInfo = SubscriptionInfo.of("topic", NAME); + compareSubscriptionInfo(SubscriptionInfo.of(TOPIC, NAME), + SubscriptionInfo.fromPb(subscriptionInfo.toPb("project"))); + subscriptionInfo = SubscriptionInfo.of(TOPIC, NAME, ENDPOINT); + compareSubscriptionInfo(subscriptionInfo, + SubscriptionInfo.fromPb(subscriptionInfo.toPb("project"))); + subscriptionInfo = SubscriptionInfo.of("topic", NAME, ENDPOINT); + compareSubscriptionInfo(SubscriptionInfo.of(TOPIC, NAME, ENDPOINT), + SubscriptionInfo.fromPb(subscriptionInfo.toPb("project"))); + com.google.pubsub.v1.Subscription subscription = SUBSCRIPTION_INFO.toPb("project"); + subscriptionInfo = + SubscriptionInfo.fromPb(subscription.toBuilder().setTopic("_deleted-topic_").build()); + assertEquals(TopicId.deletedTopic(), subscriptionInfo.getTopic()); + assertEquals(NAME, subscriptionInfo.getName()); + assertEquals(PUSH_CONFIG, subscriptionInfo.getPushConfig()); + assertEquals(ACK_DEADLINE, subscriptionInfo.getAckDeadlineSeconds()); + } + + private void compareSubscriptionInfo(SubscriptionInfo expected, SubscriptionInfo value) { + assertEquals(expected, value); + assertEquals(expected.getTopic(), value.getTopic()); + assertEquals(expected.getName(), value.getName()); + assertEquals(expected.getPushConfig(), value.getPushConfig()); + assertEquals(expected.getAckDeadlineSeconds(), value.getAckDeadlineSeconds()); + assertEquals(expected.hashCode(), value.hashCode()); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SubscriptionTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SubscriptionTest.java new file mode 100644 index 000000000000..0e6ac4cc2b61 --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/SubscriptionTest.java @@ -0,0 +1,436 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.reset; +import static org.easymock.EasyMock.verify; +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 com.google.cloud.Identity; +import com.google.cloud.Policy; +import com.google.cloud.Role; +import com.google.cloud.pubsub.PubSub.MessageConsumer; +import com.google.cloud.pubsub.PubSub.MessageProcessor; +import com.google.cloud.pubsub.PubSub.PullOption; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.Futures; + +import org.easymock.EasyMock; +import org.junit.After; +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.ExecutionException; + +public class SubscriptionTest { + + private static final TopicId TOPIC_ID = TopicId.of("project", "topic"); + private static final String NAME = "subscription"; + private static final String ENDPOINT = "https://example.com/push"; + private static final PushConfig PUSH_CONFIG = PushConfig.of(ENDPOINT); + private static final int ACK_DEADLINE = 42; + private static final SubscriptionInfo SUBSCRIPTION_INFO = + SubscriptionInfo.newBuilder(TOPIC_ID, NAME) + .setPushConfig(PUSH_CONFIG) + .setAckDeadLineSeconds(ACK_DEADLINE) + .build(); + private static final Message MESSAGE1 = Message.of("payload1"); + private static final com.google.pubsub.v1.ReceivedMessage MESSAGE_PB1 = + com.google.pubsub.v1.ReceivedMessage.newBuilder() + .setMessage(MESSAGE1.toPb()) + .setAckId("ackId1") + .build(); + private static final Message MESSAGE2 = Message.of("payload2"); + private static final com.google.pubsub.v1.ReceivedMessage MESSAGE_PB2 = + com.google.pubsub.v1.ReceivedMessage.newBuilder() + .setMessage(MESSAGE2.toPb()) + .setAckId("ackId2") + .build(); + private static final Policy POLICY = Policy.newBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build(); + + private final PubSub serviceMockReturnsOptions = createStrictMock(PubSub.class); + private final PubSubOptions mockOptions = createStrictMock(PubSubOptions.class); + private PubSub pubsub; + private Subscription expectedSubscription; + private Subscription subscription; + + private void initializeExpectedSubscription(int optionsCalls) { + expect(serviceMockReturnsOptions.getOptions()).andReturn(mockOptions).times(optionsCalls); + replay(serviceMockReturnsOptions); + pubsub = createStrictMock(PubSub.class); + expectedSubscription = new Subscription(serviceMockReturnsOptions, + new Subscription.BuilderImpl(SUBSCRIPTION_INFO)); + } + + private void initializeSubscription() { + subscription = new Subscription(pubsub, new Subscription.BuilderImpl(SUBSCRIPTION_INFO)); + } + + @After + public void tearDown() throws Exception { + verify(pubsub, serviceMockReturnsOptions); + } + + @Test + public void testBuilder() { + initializeExpectedSubscription(2); + replay(pubsub); + assertEquals(TOPIC_ID, expectedSubscription.getTopic()); + assertEquals(NAME, expectedSubscription.getName()); + assertEquals(PUSH_CONFIG, expectedSubscription.getPushConfig()); + assertEquals(ACK_DEADLINE, expectedSubscription.getAckDeadlineSeconds()); + assertSame(serviceMockReturnsOptions, expectedSubscription.getPubsub()); + Subscription builtSubscription = expectedSubscription.toBuilder() + .setName("newSubscription") + .setTopic("newProject", "newTopic") + .setPushConfig(null) + .setAckDeadLineSeconds(10) + .build(); + assertEquals(TopicId.of("newProject", "newTopic"), builtSubscription.getTopic()); + assertEquals("newSubscription", builtSubscription.getName()); + assertEquals(null, builtSubscription.getPushConfig()); + assertEquals(10, builtSubscription.getAckDeadlineSeconds()); + } + + @Test + public void testBuilderDeprecated() { + initializeExpectedSubscription(2); + replay(pubsub); + assertEquals(TOPIC_ID, expectedSubscription.topic()); + assertEquals(NAME, expectedSubscription.name()); + assertEquals(PUSH_CONFIG, expectedSubscription.pushConfig()); + assertEquals(ACK_DEADLINE, expectedSubscription.ackDeadlineSeconds()); + assertSame(serviceMockReturnsOptions, expectedSubscription.pubSub()); + Subscription builtSubscription = expectedSubscription.toBuilder() + .name("newSubscription") + .topic("newProject", "newTopic") + .pushConfig(null) + .ackDeadLineSeconds(10) + .build(); + assertEquals(TopicId.of("newProject", "newTopic"), builtSubscription.topic()); + assertEquals("newSubscription", builtSubscription.name()); + assertEquals(null, builtSubscription.pushConfig()); + assertEquals(10, builtSubscription.ackDeadlineSeconds()); + } + + @Test + public void testToBuilder() { + initializeExpectedSubscription(2); + replay(pubsub); + compareSubscription(expectedSubscription, expectedSubscription.toBuilder().build()); + } + + @Test + public void testReload() { + initializeExpectedSubscription(2); + SubscriptionInfo updatedInfo = SUBSCRIPTION_INFO.toBuilder().setName("newSubscription").build(); + Subscription expectedSubscription = + new Subscription(serviceMockReturnsOptions, new SubscriptionInfo.BuilderImpl(updatedInfo)); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getSubscription(NAME)).andReturn(expectedSubscription); + replay(pubsub); + initializeSubscription(); + Subscription updatedSubscription = subscription.reload(); + compareSubscription(expectedSubscription, updatedSubscription); + } + + @Test + public void testReloadNull() { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getSubscription(NAME)).andReturn(null); + replay(pubsub); + initializeSubscription(); + assertNull(subscription.reload()); + } + + @Test + public void testReloadAsync() throws ExecutionException, InterruptedException { + initializeExpectedSubscription(2); + SubscriptionInfo updatedInfo = SUBSCRIPTION_INFO.toBuilder().setName("newSubscription").build(); + Subscription expectedSubscription = + new Subscription(serviceMockReturnsOptions, new SubscriptionInfo.BuilderImpl(updatedInfo)); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getSubscriptionAsync(NAME)) + .andReturn(Futures.immediateFuture(expectedSubscription)); + replay(pubsub); + initializeSubscription(); + Subscription updatedSubscription = subscription.reloadAsync().get(); + compareSubscription(expectedSubscription, updatedSubscription); + } + + @Test + public void testReloadAsyncNull() throws ExecutionException, InterruptedException { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getSubscriptionAsync(NAME)) + .andReturn(Futures.immediateFuture(null)); + replay(pubsub); + initializeSubscription(); + assertNull(subscription.reloadAsync().get()); + } + + @Test + public void testDeleteTrue() { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.deleteSubscription(NAME)).andReturn(true); + replay(pubsub); + initializeSubscription(); + assertTrue(subscription.delete()); + } + + @Test + public void testDeleteFalse() { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.deleteSubscription(NAME)).andReturn(false); + replay(pubsub); + initializeSubscription(); + assertFalse(subscription.delete()); + } + + @Test + public void testDeleteAsyncTrue() throws ExecutionException, InterruptedException { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.deleteSubscriptionAsync(NAME)) + .andReturn(Futures.immediateFuture(true)); + replay(pubsub); + initializeSubscription(); + assertTrue(subscription.deleteAsync().get()); + } + + @Test + public void testDeleteAsyncFalse() throws ExecutionException, InterruptedException { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.deleteSubscriptionAsync(NAME)) + .andReturn(Futures.immediateFuture(false)); + replay(pubsub); + initializeSubscription(); + assertFalse(subscription.deleteAsync().get()); + } + + @Test + public void testReplacePushConfig() { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + PushConfig pushConfig = PushConfig.of("https://example.com/newPush"); + pubsub.replacePushConfig(NAME, pushConfig); + EasyMock.expectLastCall(); + replay(pubsub); + initializeSubscription(); + subscription.replacePushConfig(pushConfig); + } + + @Test + public void testReplacePushConfig_Null() { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + pubsub.replacePushConfig(NAME, null); + EasyMock.expectLastCall(); + replay(pubsub); + initializeSubscription(); + subscription.replacePushConfig(null); + } + + @Test + public void testReplacePushConfig_Async() throws ExecutionException, InterruptedException { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + PushConfig pushConfig = PushConfig.of("https://example.com/newPush"); + expect(pubsub.replacePushConfigAsync(NAME, pushConfig)) + .andReturn(Futures.immediateFuture(null)); + EasyMock.expectLastCall(); + replay(pubsub); + initializeSubscription(); + assertNull(subscription.replacePushConfigAsync(pushConfig).get()); + } + + @Test + public void testReplacePushConfigAsync_Null() throws ExecutionException, InterruptedException { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.replacePushConfigAsync(NAME, null)) + .andReturn(Futures.immediateFuture(null)); + replay(pubsub); + initializeSubscription(); + assertNull(subscription.replacePushConfigAsync(null).get()); + } + + @Test + public void testPull() { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions).times(2); + replay(pubsub); + ReceivedMessage message1 = ReceivedMessage.fromPb(pubsub, NAME, MESSAGE_PB1); + ReceivedMessage message2 = ReceivedMessage.fromPb(pubsub, NAME, MESSAGE_PB2); + reset(pubsub); + expect(pubsub.getOptions()).andReturn(mockOptions); + List messages = ImmutableList.of(message1, message2); + expect(pubsub.pull(NAME, 42)).andReturn(messages.iterator()); + replay(pubsub); + initializeSubscription(); + assertEquals(messages, Lists.newArrayList(subscription.pull(42))); + } + + @Test + public void testPullAsync() throws ExecutionException, InterruptedException { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions).times(2); + replay(pubsub); + ReceivedMessage message1 = ReceivedMessage.fromPb(pubsub, NAME, MESSAGE_PB1); + ReceivedMessage message2 = ReceivedMessage.fromPb(pubsub, NAME, MESSAGE_PB2); + reset(pubsub); + expect(pubsub.getOptions()).andReturn(mockOptions); + List messages = ImmutableList.of(message1, message2); + expect(pubsub.pullAsync(NAME, 42)).andReturn(Futures.immediateFuture(messages.iterator())); + replay(pubsub); + initializeSubscription(); + assertEquals(messages, Lists.newArrayList(subscription.pullAsync(42).get())); + } + + @Test + public void testMessageConsumer() throws ExecutionException, InterruptedException { + initializeExpectedSubscription(1); + MessageConsumer messageConsumer = createStrictMock(MessageConsumer.class); + MessageProcessor messageProcessor = createStrictMock(MessageProcessor.class); + replay(messageConsumer, messageProcessor); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.pullAsync(NAME, messageProcessor)).andReturn(messageConsumer); + replay(pubsub); + initializeSubscription(); + assertSame(messageConsumer, subscription.pullAsync(messageProcessor)); + verify(messageConsumer, messageProcessor); + } + + @Test + public void testMessageConsumerWithOptions() throws ExecutionException, InterruptedException { + initializeExpectedSubscription(1); + MessageConsumer messageConsumer = createStrictMock(MessageConsumer.class); + MessageProcessor messageProcessor = createStrictMock(MessageProcessor.class); + replay(messageConsumer, messageProcessor); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.pullAsync(NAME, messageProcessor, PullOption.maxQueuedCallbacks(2))) + .andReturn(messageConsumer); + replay(pubsub); + initializeSubscription(); + assertSame(messageConsumer, + subscription.pullAsync(messageProcessor, PullOption.maxQueuedCallbacks(2))); + verify(messageConsumer, messageProcessor); + } + + @Test + public void testGetPolicy() { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getSubscriptionPolicy(NAME)).andReturn(POLICY); + replay(pubsub); + initializeSubscription(); + Policy policy = subscription.getPolicy(); + assertEquals(POLICY, policy); + } + + @Test + public void testGetPolicyNull() { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getSubscriptionPolicy(NAME)).andReturn(null); + replay(pubsub); + initializeSubscription(); + assertNull(subscription.getPolicy()); + } + + @Test + public void testGetPolicyAsync() throws ExecutionException, InterruptedException { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getSubscriptionPolicyAsync(NAME)).andReturn(Futures.immediateFuture(POLICY)); + replay(pubsub); + initializeSubscription(); + Policy policy = subscription.getPolicyAsync().get(); + assertEquals(POLICY, policy); + } + + @Test + public void testReplacePolicy() { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.replaceSubscriptionPolicy(NAME, POLICY)).andReturn(POLICY); + replay(pubsub); + initializeSubscription(); + Policy policy = subscription.replacePolicy(POLICY); + assertEquals(POLICY, policy); + } + + @Test + public void testReplacePolicyAsync() throws ExecutionException, InterruptedException { + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.replaceSubscriptionPolicyAsync(NAME, POLICY)) + .andReturn(Futures.immediateFuture(POLICY)); + replay(pubsub); + initializeSubscription(); + Policy policy = subscription.replacePolicyAsync(POLICY).get(); + assertEquals(POLICY, policy); + } + + @Test + public void testTestPermissions() { + List permissions = ImmutableList.of("pubsub.subscriptions.get"); + List permissionsResult = ImmutableList.of(true); + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.testSubscriptionPermissions(NAME, permissions)).andReturn(permissionsResult); + replay(pubsub); + initializeSubscription(); + assertEquals(permissionsResult, subscription.testPermissions(permissions)); + } + + @Test + public void testTestPermissionsAsync() throws ExecutionException, InterruptedException { + List permissions = ImmutableList.of("pubsub.subscriptions.get"); + List permissionsResult = ImmutableList.of(true); + initializeExpectedSubscription(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.testSubscriptionPermissionsAsync(NAME, permissions)) + .andReturn(Futures.immediateFuture(permissionsResult)); + replay(pubsub); + initializeSubscription(); + assertEquals(permissionsResult, subscription.testPermissionsAsync(permissions).get()); + } + + private void compareSubscription(Subscription expected, Subscription value) { + assertEquals(expected, value); + assertEquals(expected.getTopic(), value.getTopic()); + assertEquals(expected.getName(), value.getName()); + assertEquals(expected.getPushConfig(), value.getPushConfig()); + assertEquals(expected.getAckDeadlineSeconds(), value.getAckDeadlineSeconds()); + assertEquals(expected.hashCode(), value.hashCode()); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/TopicIdTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/TopicIdTest.java new file mode 100644 index 000000000000..72c9fc9c212e --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/TopicIdTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +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 org.junit.Test; + +public class TopicIdTest { + + private static final String PROJECT = "project"; + private static final String NAME = "topic"; + private static final String TOPIC_PB = "projects/project/topics/topic"; + private static final String DELETED_TOPIC_NAME = "_deleted-topic_"; + + @Test + public void testOf() { + TopicId topicId = TopicId.of(PROJECT, NAME); + assertEquals(PROJECT, topicId.getProject()); + assertEquals(NAME, topicId.getTopic()); + topicId = TopicId.of(NAME); + assertNull(topicId.getProject()); + assertEquals(NAME, topicId.getTopic()); + assertFalse(topicId.isDeleted()); + } + + @Test + public void testOfDeprecated() { + TopicId topicId = TopicId.of(PROJECT, NAME); + assertEquals(PROJECT, topicId.project()); + assertEquals(NAME, topicId.topic()); + topicId = TopicId.of(NAME); + assertNull(topicId.project()); + assertEquals(NAME, topicId.topic()); + assertFalse(topicId.isDeleted()); + } + + @Test + public void testDeletedTopic() { + TopicId deletedTopic = TopicId.deletedTopic(); + assertNull(deletedTopic.getProject()); + assertEquals(DELETED_TOPIC_NAME, deletedTopic.getTopic()); + assertTrue(deletedTopic.isDeleted()); + assertSame(deletedTopic, TopicId.deletedTopic()); + } + + @Test + public void testToAndFromPb() { + TopicId topicId = TopicId.of(PROJECT, NAME); + String topicPb = topicId.toPb("otherProject"); + assertEquals(TOPIC_PB, topicPb); + compareTopicId(topicId, TopicId.fromPb(topicPb)); + topicId = TopicId.of(NAME); + topicPb = topicId.toPb("otherProject"); + assertEquals("projects/otherProject/topics/topic", topicPb); + compareTopicId(TopicId.of("otherProject", NAME), TopicId.fromPb(topicPb)); + assertSame(TopicId.deletedTopic(), TopicId.fromPb(DELETED_TOPIC_NAME)); + } + + private void compareTopicId(TopicId expected, TopicId value) { + assertEquals(expected, value); + assertEquals(expected.getProject(), value.getProject()); + assertEquals(expected.getTopic(), value.getTopic()); + assertEquals(expected.isDeleted(), value.isDeleted()); + assertEquals(expected.toPb("project"), value.toPb("project")); + assertEquals(expected.hashCode(), value.hashCode()); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/TopicInfoTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/TopicInfoTest.java new file mode 100644 index 000000000000..f89faed499c9 --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/TopicInfoTest.java @@ -0,0 +1,70 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class TopicInfoTest { + + private static final String NAME = "topic"; + private static final TopicInfo TOPIC_INFO = TopicInfo.newBuilder("topic").build(); + private static final TopicInfo DEPRECATED_TOPIC_INFO = TopicInfo.builder("topic").build(); + + @Test + public void testToBuilder() { + compareTopicInfo(TOPIC_INFO, TOPIC_INFO.toBuilder().build()); + TopicInfo topicInfo = TOPIC_INFO.toBuilder() + .setName("newTopic") + .build(); + assertEquals("newTopic", topicInfo.getName()); + topicInfo = topicInfo.toBuilder().setName(NAME).build(); + compareTopicInfo(TOPIC_INFO, topicInfo); + } + + @Test + public void testBuilder() { + assertEquals(NAME, TOPIC_INFO.getName()); + TopicInfo topicInfo = TopicInfo.newBuilder("wrongName").setName(NAME).build(); + compareTopicInfo(TOPIC_INFO, topicInfo); + } + + @Test + public void testBuilderDeprecated() { + assertEquals(NAME, DEPRECATED_TOPIC_INFO.name()); + TopicInfo topicInfo = TopicInfo.builder("wrongName").name(NAME).build(); + compareTopicInfo(TOPIC_INFO, topicInfo); + } + + @Test + public void testOf() { + compareTopicInfo(TOPIC_INFO, TopicInfo.of(NAME)); + } + + @Test + public void testToAndFromPb() { + compareTopicInfo(TOPIC_INFO, TopicInfo.fromPb(TOPIC_INFO.toPb("project"))); + assertEquals("projects/project/topics/topic", TOPIC_INFO.toPb("project").getName()); + } + + private void compareTopicInfo(TopicInfo expected, TopicInfo value) { + assertEquals(expected, value); + assertEquals(expected.getName(), value.getName()); + assertEquals(expected.hashCode(), value.hashCode()); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/TopicTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/TopicTest.java new file mode 100644 index 000000000000..438759b73184 --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/TopicTest.java @@ -0,0 +1,416 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +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 com.google.cloud.AsyncPage; +import com.google.cloud.AsyncPageImpl; +import com.google.cloud.Identity; +import com.google.cloud.Page; +import com.google.cloud.PageImpl; +import com.google.cloud.Policy; +import com.google.cloud.Role; +import com.google.cloud.pubsub.PubSub.ListOption; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.Futures; + +import org.junit.After; +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.ExecutionException; + +public class TopicTest { + + private static final String NAME = "topic"; + private static final TopicInfo TOPIC_INFO = TopicInfo.of(NAME); + private static final Policy POLICY = Policy.newBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build(); + + private final PubSub serviceMockReturnsOptions = createStrictMock(PubSub.class); + private final PubSubOptions mockOptions = createMock(PubSubOptions.class); + private PubSub pubsub; + private Topic expectedTopic; + private Topic topic; + + private void initializeExpectedTopic(int optionsCalls) { + expect(serviceMockReturnsOptions.getOptions()).andReturn(mockOptions).times(optionsCalls); + replay(serviceMockReturnsOptions); + pubsub = createStrictMock(PubSub.class); + expectedTopic = new Topic(serviceMockReturnsOptions, new Topic.BuilderImpl(TOPIC_INFO)); + } + + private void initializeTopic() { + topic = new Topic(pubsub, new Topic.BuilderImpl(TOPIC_INFO)); + } + + @After + public void tearDown() throws Exception { + verify(pubsub, serviceMockReturnsOptions); + } + + @Test + public void testBuilder() { + initializeExpectedTopic(2); + replay(pubsub); + Topic builtTopic = expectedTopic.toBuilder().setName("newTopic").build(); + assertEquals("newTopic", builtTopic.getName()); + assertSame(serviceMockReturnsOptions, expectedTopic.getPubsub()); + } + + @Test + public void testBuilderDeprecated() { + initializeExpectedTopic(2); + replay(pubsub); + Topic builtTopic = expectedTopic.toBuilder().name("newTopic").build(); + assertEquals("newTopic", builtTopic.name()); + assertSame(serviceMockReturnsOptions, expectedTopic.pubSub()); + } + + @Test + public void testToBuilder() { + initializeExpectedTopic(2); + replay(pubsub); + compareTopic(expectedTopic, expectedTopic.toBuilder().build()); + } + + @Test + public void testReload() { + initializeExpectedTopic(2); + TopicInfo updatedInfo = TOPIC_INFO.toBuilder().setName("newTopic").build(); + Topic expectedTopic = + new Topic(serviceMockReturnsOptions, new TopicInfo.BuilderImpl(updatedInfo)); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getTopic(NAME)).andReturn(expectedTopic); + replay(pubsub); + initializeTopic(); + Topic updatedTopic = topic.reload(); + compareTopic(expectedTopic, updatedTopic); + } + + @Test + public void testReloadNull() { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getTopic(NAME)).andReturn(null); + replay(pubsub); + initializeTopic(); + assertNull(topic.reload()); + } + + @Test + public void testReloadAsync() throws ExecutionException, InterruptedException { + initializeExpectedTopic(2); + TopicInfo updatedInfo = TOPIC_INFO.toBuilder().setName("newTopic").build(); + Topic expectedTopic = + new Topic(serviceMockReturnsOptions, new TopicInfo.BuilderImpl(updatedInfo)); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getTopicAsync(NAME)) + .andReturn(Futures.immediateFuture(expectedTopic)); + replay(pubsub); + initializeTopic(); + Topic updatedTopic = topic.reloadAsync().get(); + compareTopic(expectedTopic, updatedTopic); + } + + @Test + public void testReloadAsyncNull() throws ExecutionException, InterruptedException { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getTopicAsync(NAME)).andReturn(Futures.immediateFuture(null)); + replay(pubsub); + initializeTopic(); + assertNull(topic.reloadAsync().get()); + } + + @Test + public void testDeleteTrue() { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.deleteTopic(NAME)).andReturn(true); + replay(pubsub); + initializeTopic(); + assertTrue(topic.delete()); + } + + @Test + public void testDeleteFalse() { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.deleteTopic(NAME)).andReturn(false); + replay(pubsub); + initializeTopic(); + assertFalse(topic.delete()); + } + + @Test + public void testDeleteAsyncTrue() throws ExecutionException, InterruptedException { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.deleteTopicAsync(NAME)).andReturn(Futures.immediateFuture(true)); + replay(pubsub); + initializeTopic(); + assertTrue(topic.deleteAsync().get()); + } + + @Test + public void testDeleteAsyncFalse() throws ExecutionException, InterruptedException { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.deleteTopicAsync(NAME)).andReturn(Futures.immediateFuture(false)); + replay(pubsub); + initializeTopic(); + assertFalse(topic.deleteAsync().get()); + } + + @Test + public void testPublishOneMessage() { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + Message message = Message.of("payload1"); + String messageId = "messageId"; + expect(pubsub.publish(NAME, message)).andReturn(messageId); + replay(pubsub); + initializeTopic(); + assertEquals(messageId, topic.publish(message)); + } + + @Test + public void testPublishOneMessageAsync() throws ExecutionException, InterruptedException { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + Message message = Message.of("payload1"); + String messageId = "messageId"; + expect(pubsub.publishAsync(NAME, message)) + .andReturn(Futures.immediateFuture(messageId)); + replay(pubsub); + initializeTopic(); + assertEquals(messageId, topic.publishAsync(message).get()); + } + + @Test + public void testPublishMoreMessages() { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + List messageIds = ImmutableList.of("messageId1", "messageId2"); + expect(pubsub.publish(NAME, message1, message2)).andReturn(messageIds); + replay(pubsub); + initializeTopic(); + assertEquals(messageIds, topic.publish(message1, message2)); + } + + @Test + public void testPublishMoreMessagesAsync() throws ExecutionException, InterruptedException { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + List messageIds = ImmutableList.of("messageId1", "messageId2"); + expect(pubsub.publishAsync(NAME, message1, message2)) + .andReturn(Futures.immediateFuture(messageIds)); + replay(pubsub); + initializeTopic(); + assertEquals(messageIds, topic.publishAsync(message1, message2).get()); + } + + @Test + public void testPublishMessageList() { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + List messages = ImmutableList.of(message1, message2); + List messageIds = ImmutableList.of("messageId1", "messageId2"); + expect(pubsub.publish(NAME, messages)).andReturn(messageIds); + replay(pubsub); + initializeTopic(); + assertEquals(messageIds, topic.publish(messages)); + } + + @Test + public void testPublishMessageListAsync() throws ExecutionException, InterruptedException { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + Message message1 = Message.of("payload1"); + Message message2 = Message.of("payload2"); + List messages = ImmutableList.of(message1, message2); + List messageIds = ImmutableList.of("messageId1", "messageId2"); + expect(pubsub.publishAsync(NAME, messages)) + .andReturn(Futures.immediateFuture(messageIds)); + replay(pubsub); + initializeTopic(); + assertEquals(messageIds, topic.publishAsync(messages).get()); + } + + @Test + public void testListSubscriptions() { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + final List subscriptions = ImmutableList.of( + new SubscriptionId("project", "subscription1"), + new SubscriptionId("project", "subscription2")); + Page result = new PageImpl<>(null, null, subscriptions); + expect(pubsub.listSubscriptions(NAME)).andReturn(result); + replay(pubsub); + initializeTopic(); + assertEquals(subscriptions, topic.listSubscriptions().getValues()); + } + + @Test + public void testListSubscriptionsWithOptions() { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + final List subscriptions = ImmutableList.of( + new SubscriptionId("project", "subscription1"), + new SubscriptionId("project", "subscription2")); + Page result = new PageImpl<>(null, null, subscriptions); + expect(pubsub.listSubscriptions(NAME, ListOption.pageSize(42))).andReturn(result); + replay(pubsub); + initializeTopic(); + assertEquals(subscriptions, topic.listSubscriptions(ListOption.pageSize(42)).getValues()); + } + + @Test + public void testListSubscriptionsAsync() throws ExecutionException, InterruptedException { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + final List subscriptions = ImmutableList.of( + new SubscriptionId("project", "subscription1"), + new SubscriptionId("project", "subscription2")); + AsyncPage result = new AsyncPageImpl<>(null, null, subscriptions); + expect(pubsub.listSubscriptionsAsync(NAME)) + .andReturn(Futures.immediateFuture(result)); + replay(pubsub); + initializeTopic(); + assertEquals(subscriptions, topic.listSubscriptionsAsync().get().getValues()); + } + + @Test + public void testListSubscriptionsAsyncWithOptions() + throws ExecutionException, InterruptedException { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + final List subscriptions = ImmutableList.of( + new SubscriptionId("project", "subscription1"), + new SubscriptionId("project", "subscription2")); + AsyncPage result = new AsyncPageImpl<>(null, null, subscriptions); + expect(pubsub.listSubscriptionsAsync(NAME, ListOption.pageSize(42))) + .andReturn(Futures.immediateFuture(result)); + replay(pubsub); + initializeTopic(); + assertEquals(subscriptions, + topic.listSubscriptionsAsync(ListOption.pageSize(42)).get().getValues()); + } + + @Test + public void testGetPolicy() { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getTopicPolicy(NAME)).andReturn(POLICY); + replay(pubsub); + initializeTopic(); + Policy policy = topic.getPolicy(); + assertEquals(POLICY, policy); + } + + @Test + public void testGetPolicyNull() { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getTopicPolicy(NAME)).andReturn(null); + replay(pubsub); + initializeTopic(); + assertNull(topic.getPolicy()); + } + + @Test + public void testGetPolicyAsync() throws ExecutionException, InterruptedException { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.getTopicPolicyAsync(NAME)).andReturn(Futures.immediateFuture(POLICY)); + replay(pubsub); + initializeTopic(); + Policy policy = topic.getPolicyAsync().get(); + assertEquals(POLICY, policy); + } + + @Test + public void testReplacePolicy() { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.replaceTopicPolicy(NAME, POLICY)).andReturn(POLICY); + replay(pubsub); + initializeTopic(); + Policy policy = topic.replacePolicy(POLICY); + assertEquals(POLICY, policy); + } + + @Test + public void testReplacePolicyAsync() throws ExecutionException, InterruptedException { + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.replaceTopicPolicyAsync(NAME, POLICY)).andReturn(Futures.immediateFuture(POLICY)); + replay(pubsub); + initializeTopic(); + Policy policy = topic.replacePolicyAsync(POLICY).get(); + assertEquals(POLICY, policy); + } + + @Test + public void testTestPermissions() { + List permissions = ImmutableList.of("pubsub.topics.get"); + List permissionsResult = ImmutableList.of(true); + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.testTopicPermissions(NAME, permissions)).andReturn(permissionsResult); + replay(pubsub); + initializeTopic(); + assertEquals(permissionsResult, topic.testPermissions(permissions)); + } + + @Test + public void testTestPermissionsAsync() throws ExecutionException, InterruptedException { + List permissions = ImmutableList.of("pubsub.topics.get"); + List permissionsResult = ImmutableList.of(true); + initializeExpectedTopic(1); + expect(pubsub.getOptions()).andReturn(mockOptions); + expect(pubsub.testTopicPermissionsAsync(NAME, permissions)) + .andReturn(Futures.immediateFuture(permissionsResult)); + replay(pubsub); + initializeTopic(); + assertEquals(permissionsResult, topic.testPermissionsAsync(permissions).get()); + } + + private void compareTopic(Topic expected, Topic value) { + assertEquals(expected, value); + assertEquals(expected.getName(), value.getName()); + assertEquals(expected.hashCode(), value.hashCode()); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/it/ITPubSubTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/it/ITPubSubTest.java new file mode 100644 index 000000000000..9f9ccdecee25 --- /dev/null +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/it/ITPubSubTest.java @@ -0,0 +1,144 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub.it; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.Identity; +import com.google.cloud.Policy; +import com.google.cloud.Role; +import com.google.cloud.pubsub.BaseSystemTest; +import com.google.cloud.pubsub.PubSub; +import com.google.cloud.pubsub.PubSubOptions; +import com.google.cloud.pubsub.Subscription; +import com.google.cloud.pubsub.SubscriptionInfo; +import com.google.cloud.pubsub.Topic; +import com.google.cloud.pubsub.TopicInfo; +import com.google.common.collect.ImmutableList; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; + +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +public class ITPubSubTest extends BaseSystemTest { + + private static final PubSub PUB_SUB = PubSubOptions.getDefaultInstance().getService(); + private static final String NAME_SUFFIX = UUID.randomUUID().toString(); + + @Rule + public Timeout globalTimeout = Timeout.seconds(300); + + @Override + protected PubSub pubsub() { + return PUB_SUB; + } + + @Override + protected String formatForTest(String resourceName) { + return resourceName + "-" + NAME_SUFFIX; + } + + // Policy tests are defined here and not in BaseSystemTest because Pub/Sub emulator does not + // support IAM yet + + @Test + public void testTopicPolicy() { + String topicName = formatForTest("test-topic-policy"); + Topic topic = pubsub().create(TopicInfo.of(topicName)); + Policy policy = pubsub().getTopicPolicy(topicName); + policy = pubsub().replaceTopicPolicy(topicName, policy.toBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build()); + assertTrue(policy.getBindings().containsKey(Role.viewer())); + assertTrue(policy.getBindings().get(Role.viewer()).contains(Identity.allAuthenticatedUsers())); + List permissions = + pubsub().testTopicPermissions(topicName, ImmutableList.of("pubsub.topics.get")); + assertTrue(permissions.get(0)); + topic.delete(); + } + + @Test + public void testNonExistingTopicPolicy() { + String topicName = formatForTest("test-non-existing-topic-policy"); + assertNull(pubsub().getTopicPolicy(topicName)); + } + + @Test + public void testTopicPolicyAsync() throws ExecutionException, InterruptedException { + String topicName = formatForTest("test-topic-policy-async"); + Topic topic = pubsub().create(TopicInfo.of(topicName)); + Policy policy = pubsub().getTopicPolicyAsync(topicName).get(); + policy = pubsub().replaceTopicPolicyAsync(topicName, policy.toBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build()).get(); + assertTrue(policy.getBindings().containsKey(Role.viewer())); + assertTrue(policy.getBindings().get(Role.viewer()).contains(Identity.allAuthenticatedUsers())); + List permissions = + pubsub().testTopicPermissionsAsync(topicName, ImmutableList.of("pubsub.topics.get")).get(); + assertTrue(permissions.get(0)); + topic.delete(); + } + + @Test + public void testSubscriptionPolicy() { + String topicName = formatForTest("test-subscription-policy"); + Topic topic = pubsub().create(TopicInfo.of(topicName)); + String subscriptionName = formatForTest("test-subscription-policy"); + Subscription subscription = pubsub().create(SubscriptionInfo.of(topicName, subscriptionName)); + Policy policy = pubsub().getSubscriptionPolicy(subscriptionName); + policy = pubsub().replaceSubscriptionPolicy(subscriptionName, policy.toBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build()); + assertTrue(policy.getBindings().containsKey(Role.viewer())); + assertTrue(policy.getBindings().get(Role.viewer()).contains(Identity.allAuthenticatedUsers())); + List permissions = pubsub().testSubscriptionPermissions(subscriptionName, + ImmutableList.of("pubsub.subscriptions.get")); + assertTrue(permissions.get(0)); + topic.delete(); + subscription.delete(); + } + + @Test + public void testSubscriptionPolicyAsync() throws ExecutionException, InterruptedException { + String topicName = formatForTest("test-subscription-policy-async"); + Topic topic = pubsub().create(TopicInfo.of(topicName)); + String subscriptionName = formatForTest("test-subscription-policy-async"); + Subscription subscription = pubsub().create(SubscriptionInfo.of(topicName, subscriptionName)); + Policy policy = pubsub().getSubscriptionPolicyAsync(subscriptionName).get(); + policy = pubsub().replaceSubscriptionPolicyAsync(subscriptionName, policy.toBuilder() + .addIdentity(Role.viewer(), Identity.allAuthenticatedUsers()) + .build()).get(); + assertTrue(policy.getBindings().containsKey(Role.viewer())); + assertTrue(policy.getBindings().get(Role.viewer()).contains(Identity.allAuthenticatedUsers())); + List permissions = pubsub().testSubscriptionPermissionsAsync(subscriptionName, + ImmutableList.of("pubsub.subscriptions.get")).get(); + assertTrue(permissions.get(0)); + topic.delete(); + subscription.delete(); + } + + @Test + public void testNonExistingSubscriptionPolicy() { + String subscriptionName = formatForTest("test-non-existing-subscription-policy"); + assertNull(pubsub().getSubscriptionPolicy(subscriptionName)); + } +} diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/FakeClock.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/FakeClock.java index 90349ecaa52d..76bf3f8e570f 100644 --- a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/FakeClock.java +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/FakeClock.java @@ -20,15 +20,13 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -/** - * A Clock to help with testing time-based logic. - */ -class FakeClock extends Clock { +/** A Clock to help with testing time-based logic. */ +public class FakeClock extends Clock { private final AtomicLong millis = new AtomicLong(); // Advances the clock value by {@code time} in {@code timeUnit}. - void advance(long time, TimeUnit timeUnit) { + public void advance(long time, TimeUnit timeUnit) { millis.addAndGet(timeUnit.toMillis(time)); } diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/PublisherImplTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/PublisherImplTest.java index f0a14b6c3466..b01e7a4a3a6d 100644 --- a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/PublisherImplTest.java +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/spi/v1/PublisherImplTest.java @@ -23,14 +23,13 @@ import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.times; -import com.google.api.gax.bundling.FlowController; import com.google.api.gax.grpc.BundlingSettings; import com.google.api.gax.grpc.ChannelProvider; import com.google.api.gax.grpc.ExecutorProvider; import com.google.api.gax.grpc.FixedExecutorProvider; +import com.google.api.gax.grpc.FlowControlSettings; import com.google.api.gax.grpc.InstantiatingExecutorProvider; import com.google.cloud.pubsub.spi.v1.Publisher.Builder; -import com.google.common.base.Optional; import com.google.common.util.concurrent.ListenableFuture; import com.google.protobuf.ByteString; import com.google.pubsub.v1.PublishRequest; @@ -382,9 +381,9 @@ public void testPublisherGetters() throws Exception { .setElementCountThreshold(12) .build()); builder.setFlowControlSettings( - FlowController.Settings.newBuilder() - .setMaxOutstandingRequestBytes(Optional.of(13)) - .setMaxOutstandingElementCount(Optional.of(14)) + FlowControlSettings.newBuilder() + .setMaxOutstandingRequestBytes(13) + .setMaxOutstandingElementCount(14) .build()); Publisher publisher = builder.build(); @@ -392,10 +391,8 @@ public void testPublisherGetters() throws Exception { assertEquals(10, (long) publisher.getBundlingSettings().getRequestByteThreshold()); assertEquals(new Duration(11), publisher.getBundlingSettings().getDelayThreshold()); assertEquals(12, (long) publisher.getBundlingSettings().getElementCountThreshold()); - assertEquals( - Optional.of(13), publisher.getFlowControlSettings().getMaxOutstandingRequestBytes()); - assertEquals( - Optional.of(14), publisher.getFlowControlSettings().getMaxOutstandingElementCount()); + assertEquals(13, (long) publisher.getFlowControlSettings().getMaxOutstandingRequestBytes()); + assertEquals(14, (long) publisher.getFlowControlSettings().getMaxOutstandingElementCount()); assertTrue(publisher.failOnFlowControlLimits()); publisher.shutdown(); } @@ -414,7 +411,7 @@ public void testBuilderParametersAndDefaults() { assertEquals( Publisher.Builder.DEFAULT_ELEMENT_COUNT_THRESHOLD, builder.bundlingSettings.getElementCountThreshold().longValue()); - assertEquals(FlowController.Settings.DEFAULT, builder.flowControlSettings); + assertEquals(FlowControlSettings.getDefaultInstance(), builder.flowControlSettings); assertEquals(Publisher.Builder.DEFAULT_RETRY_SETTINGS, builder.retrySettings); } @@ -526,15 +523,15 @@ public void testBuilderInvalidArguments() { } builder.setFlowControlSettings( - FlowController.Settings.DEFAULT + FlowControlSettings.getDefaultInstance() .toBuilder() - .setMaxOutstandingRequestBytes(Optional.of(1)) + .setMaxOutstandingRequestBytes(1) .build()); try { builder.setFlowControlSettings( - FlowController.Settings.DEFAULT + FlowControlSettings.getDefaultInstance() .toBuilder() - .setMaxOutstandingRequestBytes(Optional.of(0)) + .setMaxOutstandingRequestBytes(0) .build()); fail("Should have thrown an IllegalArgumentException"); } catch (IllegalArgumentException expected) { @@ -542,9 +539,9 @@ public void testBuilderInvalidArguments() { } try { builder.setFlowControlSettings( - FlowController.Settings.DEFAULT + FlowControlSettings.getDefaultInstance() .toBuilder() - .setMaxOutstandingRequestBytes(Optional.of(-1)) + .setMaxOutstandingRequestBytes(-1) .build()); fail("Should have thrown an IllegalArgumentException"); } catch (IllegalArgumentException expected) { @@ -552,15 +549,15 @@ public void testBuilderInvalidArguments() { } builder.setFlowControlSettings( - FlowController.Settings.DEFAULT + FlowControlSettings.getDefaultInstance() .toBuilder() - .setMaxOutstandingElementCount(Optional.of(1)) + .setMaxOutstandingElementCount(1) .build()); try { builder.setFlowControlSettings( - FlowController.Settings.DEFAULT + FlowControlSettings.getDefaultInstance() .toBuilder() - .setMaxOutstandingElementCount(Optional.of(0)) + .setMaxOutstandingElementCount(0) .build()); fail("Should have thrown an IllegalArgumentException"); } catch (IllegalArgumentException expected) { @@ -568,9 +565,9 @@ public void testBuilderInvalidArguments() { } try { builder.setFlowControlSettings( - FlowController.Settings.DEFAULT + FlowControlSettings.getDefaultInstance() .toBuilder() - .setMaxOutstandingElementCount(Optional.of(-1)) + .setMaxOutstandingElementCount(-1) .build()); fail("Should have thrown an IllegalArgumentException"); } catch (IllegalArgumentException expected) { diff --git a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/testing/LocalPubSubHelperTest.java b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/testing/LocalPubSubHelperTest.java index 2e1cc0cbd0dc..143f646b5a2e 100644 --- a/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/testing/LocalPubSubHelperTest.java +++ b/google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/testing/LocalPubSubHelperTest.java @@ -22,6 +22,10 @@ import static org.junit.Assert.assertTrue; import com.google.cloud.NoCredentials; +import com.google.cloud.pubsub.PubSub; +import com.google.cloud.pubsub.PubSubException; +import com.google.cloud.pubsub.PubSubOptions; +import com.google.cloud.pubsub.TopicInfo; import org.joda.time.Duration; import org.junit.Rule; @@ -45,11 +49,26 @@ public void testCreate() { assertTrue(helper.getProjectId().startsWith(PROJECT_ID_PREFIX)); } + @Test + public void testOptions() { + LocalPubSubHelper helper = LocalPubSubHelper.create(); + PubSubOptions options = helper.getOptions(); + assertTrue(options.getProjectId().startsWith(PROJECT_ID_PREFIX)); + assertTrue(options.getHost().startsWith("localhost:")); + assertSame(NoCredentials.getInstance(), options.getCredentials()); + } + @Test public void testStartStopReset() throws IOException, InterruptedException, TimeoutException { LocalPubSubHelper helper = LocalPubSubHelper.create(); helper.start(); + PubSub pubsub = helper.getOptions().getService(); + pubsub.create(TopicInfo.of(TOPIC)); + assertNotNull(pubsub.getTopic(TOPIC)); helper.reset(); + assertNull(pubsub.getTopic(TOPIC)); helper.stop(Duration.standardMinutes(1)); + thrown.expect(PubSubException.class); + pubsub.getTopic(TOPIC); } } diff --git a/google-cloud-resourcemanager/README.md b/google-cloud-resourcemanager/README.md index 8a3f06dbff13..fd3e188a3e4f 100644 --- a/google-cloud-resourcemanager/README.md +++ b/google-cloud-resourcemanager/README.md @@ -252,3 +252,4 @@ Apache 2.0 - See [LICENSE] for more information. [cloud-platform]: https://cloud.google.com/ [cloud-resourcemanager]: https://cloud.google.com/resource-manager/docs [resourcemanager-api]: https://googlecloudplatform.github.io/google-cloud-java/apidocs/index.html?com/google/cloud/resourcemanager/package-summary.html + diff --git a/google-cloud/README.md b/google-cloud/README.md index a350428faf87..1584a288e364 100644 --- a/google-cloud/README.md +++ b/google-cloud/README.md @@ -14,8 +14,8 @@ Java idiomatic client for [Google Cloud Platform][cloud-platform] services. This client supports the following Google Cloud Platform services: -- [Google Cloud Datastore] (https://cloud.google.com/datastore/) [datastore documentation][datastore-api] -- [Google Cloud Storage] (https://cloud.google.com/storage/) [storage documentation][storage-api] +- [Google Cloud Datastore] (https://cloud.google.com/datastore/) [datastore documentation][datastore-api] +- [Google Cloud Storage] (https://cloud.google.com/storage/) [storage documentation][storage-api] > Note: This client is a work-in-progress, and may occasionally > make backwards-incompatible changes. diff --git a/pom.xml b/pom.xml index 4e15214ebe17..e6538721c1f5 100644 --- a/pom.xml +++ b/pom.xml @@ -175,7 +175,7 @@ [1.7,) - + diff --git a/utilities/verify.sh b/utilities/verify.sh index d86239c5d6cb..81ed99061289 100755 --- a/utilities/verify.sh +++ b/utilities/verify.sh @@ -10,7 +10,7 @@ if [ "${TRAVIS_PULL_REQUEST}" == "false" ]; then chmod 700 $TRAVIS_BUILD_DIR/signing-tools tar xvf $TRAVIS_BUILD_DIR/signing-tools.tar -C $TRAVIS_BUILD_DIR/signing-tools # Run verify - mvn verify -Djava.util.logging.config.file=logging.properties -P release + mvn verify --quiet -Djava.util.logging.config.file=logging.properties -P release else - mvn verify -Djava.util.logging.config.file=logging.properties -DskipITs -P release + mvn verify --quiet -Djava.util.logging.config.file=logging.properties -DskipITs -P release fi