From 1435445b7d33c69f19213bfedbcc48810718b368 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Sun, 28 May 2017 00:55:20 -0700 Subject: [PATCH 01/20] initial commit iap samples --- appengine/iap/README.md | 27 +++ appengine/iap/pom.xml | 57 ++++++ .../com/example/appengine/iap/JwtServlet.java | 34 ++++ .../src/main/webapp/WEB-INF/appengine-web.xml | 18 ++ appengine/iap/src/main/webapp/WEB-INF/web.xml | 14 ++ iap/README.md | 39 +++++ iap/pom.xml | 72 ++++++++ .../java/com/example/iap/BuildIapRequest.java | 135 +++++++++++++++ .../example/iap/VerifyIapRequestHeader.java | 162 ++++++++++++++++++ 9 files changed, 558 insertions(+) create mode 100644 appengine/iap/README.md create mode 100644 appengine/iap/pom.xml create mode 100644 appengine/iap/src/main/java/com/example/appengine/iap/JwtServlet.java create mode 100644 appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml create mode 100644 appengine/iap/src/main/webapp/WEB-INF/web.xml create mode 100644 iap/README.md create mode 100644 iap/pom.xml create mode 100644 iap/src/main/java/com/example/iap/BuildIapRequest.java create mode 100644 iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java diff --git a/appengine/iap/README.md b/appengine/iap/README.md new file mode 100644 index 00000000000..ffa15659128 --- /dev/null +++ b/appengine/iap/README.md @@ -0,0 +1,27 @@ +# Identity-Aware Proxy sample for Google App Engine + +This sample demonstrates how to use the [Identity-Aware Proxy][iap-docs] on [Google App +Engine][ae-docs]. + +[iap-docs]: https://cloud.google.com/iap/docs/ +[ae-docs]: https://cloud.google.com/appengine/docs/java/ + +## Running locally + +This application depends on being enabled behind an IAP, so this program should not be run locally. + +## Deploying + +- Update [appengine-web.xml](src/main/test/app/src/main/webapp/WEB-INF/appengine-web.xml) with your project-id +- Deploy the application to the project + ``` + mvn clean appengine:update + ``` +- [Enable](https://cloud.google.com/iap/docs/app-engine-quickstart) Identity-Aware Proxy on the App Engine app. +- Add the email account you'll be running the test as to the Identity-Aware Proxy access list for the project. + +## Test + +Once deployed, access `https://your-project-id.appspot.com` . This should now prompt you to sign in for access. +Sign in with the email account that was added to the Identity-Aware proxy access list. +You should now see the jwt token that was received from the IAP server. diff --git a/appengine/iap/pom.xml b/appengine/iap/pom.xml new file mode 100644 index 00000000000..8afb4306b44 --- /dev/null +++ b/appengine/iap/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + war + 1.0-SNAPSHOT + com.example.appengine + iap-demo + + + com.google.cloud + appengine-doc-samples + 1.0.0 + .. + + + + javax.servlet + servlet-api + 2.5 + provided + + + + + ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + + org.apache.maven.plugins + 3.3 + maven-compiler-plugin + + 1.7 + 1.7 + + + + com.google.appengine + appengine-maven-plugin + ${appengine.sdk.version} + + + + diff --git a/appengine/iap/src/main/java/com/example/appengine/iap/JwtServlet.java b/appengine/iap/src/main/java/com/example/appengine/iap/JwtServlet.java new file mode 100644 index 00000000000..b6f7d95584f --- /dev/null +++ b/appengine/iap/src/main/java/com/example/appengine/iap/JwtServlet.java @@ -0,0 +1,34 @@ +/** + * Copyright 2017 Google Inc. + * + *

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.example.appengine.iap; + +import java.io.IOException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Identity Aware Proxy (IAP) Test application to reflect jwt token issued by IAP. IAP must be + * enabled on application. {@see https://cloud.google.com/iap/docs/app-engine-quickstart} + */ +@SuppressWarnings("serial") +public class JwtServlet extends HttpServlet { + + private static final String IAP_JWT_HEADER = "x-goog-authenticated-user-jwt"; + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + resp.getWriter().print(IAP_JWT_HEADER + ":" + req.getHeader(IAP_JWT_HEADER)); + } +} diff --git a/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml b/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 00000000000..a48cd61cf83 --- /dev/null +++ b/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,18 @@ + + + + ja-test-iap-2 + alpha-001 + true + diff --git a/appengine/iap/src/main/webapp/WEB-INF/web.xml b/appengine/iap/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..b4a68e55196 --- /dev/null +++ b/appengine/iap/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,14 @@ + + + + hello + com.example.appengine.iap.JwtServlet + + + hello + / + + diff --git a/iap/README.md b/iap/README.md new file mode 100644 index 00000000000..2804b3c7173 --- /dev/null +++ b/iap/README.md @@ -0,0 +1,39 @@ +# Cloud Identity-Aware Proxy Java Samples +Cloud Identity-Aware Proxy (Cloud IAP) lets you manage access to applications running in Compute Engine, App Engine standard environment, and Container Engine. Cloud IAP establishes a central authorization layer for applications accessed by HTTPS, enabling you to adopt an application-level access control model instead of relying on network-level firewalls. When you enable Cloud IAP, you must also use signed headers or the App Engine standard environment Users API to secure your app. +## Setup +- A Google Cloud project with billing enabled +- [Create an App engine service account](https://cloud.google.com/docs/authentication#getting_credentials_for_server-centric_flow) and download the credentials file as JSON. +- Install the [Google Cloud SDK](https://cloud.google.com/sdk/) and run: +``` + gcloud init + gcloud app create +``` + +## Description + +- [BuildIapRequest.java](src/main/java/com/example/iap/BuildIapRequest.java) demonstrates how to set the +`Authorization : Bearer` header to authorize access to an IAP protected URL. +- [VerifyIapRequestHeader.java](src/main/java/com/example/iap/VerifyIapRequestHeader.java) demonstrates how to +verify the JWT token in an incoming request to an IAP protected resource. + +## Testing +- Deploy the [demo app engine application](../appengine/iap/README.md). This application will return the JWT token to an authorized incoming request. +It will be used to test both the authorization of an incoming request to an IAP protected resource and the JWT token returned from IAP. + - Update [appengine-web.xml](../appengine/src/main/webapp/WEB-INF/appengine-web.xml) + with your project-id + - Deploy the application to the project + ``` + mvn clean appengine:update + ``` + - [Enable](https://cloud.google.com/iap/docs/app-engine-quickstart) Identity-Aware Proxy on the App Engine app. +- Set the environment variable `GOOGLE_APPLICATION_CREDENTIALS` to point to the service account credentials file +- Add the service account email you'll be running the test as to the Identity-Aware Proxy access list for the project. +- Set the environment variable `IAP_PROTECTED_URL` to point to `https://your-project-id.appspot.com` +- Run the integration test: +``` + mvn -Dtest=com.example.iap.BuildAndVerifyIapRequestIT verify +``` + +## References +[JWT library for Java](https://github.com/auth0/java-jwt) +[Cloud IAP docs](https://cloud.google.com/iap/docs/) diff --git a/iap/pom.xml b/iap/pom.xml new file mode 100644 index 00000000000..6767d64a7f1 --- /dev/null +++ b/iap/pom.xml @@ -0,0 +1,72 @@ + + + + + + 4.0.0 + jar + + com.example + iap-samples + 1.0-SNAPSHOT + + + 1.8 + 1.8 + UTF-8 + 9.4.3.v20170317 + + + + + + com.fasterxml.jackson.core + jackson-core + 2.8.6 + + + + + + + javax.servlet + javax.servlet-api + 3.1.0 + + + + + com.google.auth + google-auth-library-oauth2-http + 0.6.0 + + + com.auth0 + java-jwt + 3.2.0 + + + + + + junit + junit + 4.12 + + + + diff --git a/iap/src/main/java/com/example/iap/BuildIapRequest.java b/iap/src/main/java/com/example/iap/BuildIapRequest.java new file mode 100644 index 00000000000..c5467f37d62 --- /dev/null +++ b/iap/src/main/java/com/example/iap/BuildIapRequest.java @@ -0,0 +1,135 @@ +/** + * Copyright 2017 Google Inc. + * + *

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.example.iap; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpHeaders; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpRequestFactory; +import com.google.api.client.http.HttpResponse; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.UrlEncodedContent; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.JsonObjectParser; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.client.util.GenericData; +import com.google.auth.oauth2.GoogleCredentials; +import com.google.auth.oauth2.ServiceAccountCredentials; +import java.io.IOException; +import java.net.URL; +import java.security.interfaces.RSAPrivateKey; +import java.time.Clock; +import java.time.Instant; +import java.util.Collections; +import java.util.Date; + +public class BuildIapRequest { + // [START generate_iap_request] + private static final String IAM_SCOPE = "https://www.googleapis.com/auth/iam"; + private static final String OAUTH_TOKEN_URI = "https://www.googleapis.com/oauth2/v4/token"; + private static final String JWT_BEARER_TOKEN_GRANT_TYPE = + "urn:ietf:params:oauth:grant-type:jwt-bearer"; + private static final long EXPIRATION_TIME_IN_SECONDS = 3600L; + + private static final HttpTransport httpTransport = new NetHttpTransport(); + + private static Clock clock = Clock.systemUTC(); + + private BuildIapRequest() {} + + private static String getBaseUrl(URL url) throws Exception { + String urlFilePath = url.getFile(); + int pathDelim = urlFilePath.lastIndexOf('/'); + String path = (pathDelim > 0) ? urlFilePath.substring(0, pathDelim) : ""; + return (url.getProtocol() + "://" + url.getHost() + path).trim(); + } + + private static ServiceAccountCredentials getCredentials() throws Exception { + GoogleCredentials credentials = + GoogleCredentials.getApplicationDefault().createScoped(Collections.singleton(IAM_SCOPE)); + // service account credentials are required to sign the jwt token + if (credentials == null || !(credentials instanceof ServiceAccountCredentials)) { + throw new Exception("Google credentials : service accounts credentials expected"); + } + return (ServiceAccountCredentials) credentials; + } + + private static String getSignedJWToken(ServiceAccountCredentials credentials, String baseUrl) + throws IOException { + Instant now = Instant.now(clock); + long expirationTime = now.getEpochSecond() + EXPIRATION_TIME_IN_SECONDS; + // generate jwt signed by service account + return JWT.create() + .withKeyId(credentials.getPrivateKeyId()) + .withAudience(OAUTH_TOKEN_URI) + .withIssuer(credentials.getClientEmail()) + .withSubject(credentials.getClientEmail()) + .withIssuedAt(Date.from(now)) + .withExpiresAt(Date.from(Instant.ofEpochSecond(expirationTime))) + .withClaim("target_audience", baseUrl) + .sign(Algorithm.RSA256(null, (RSAPrivateKey) credentials.getPrivateKey())); + } + + private static String getGoogleIdToken(String jwt) throws Exception { + final GenericData tokenRequest = + new GenericData().set("grant_type", JWT_BEARER_TOKEN_GRANT_TYPE).set("assertion", jwt); + final UrlEncodedContent content = new UrlEncodedContent(tokenRequest); + + final HttpRequestFactory requestFactory = httpTransport.createRequestFactory(); + + final HttpRequest request = + requestFactory + .buildPostRequest(new GenericUrl(OAUTH_TOKEN_URI), content) + .setParser(new JsonObjectParser(JacksonFactory.getDefaultInstance())); + + HttpResponse response; + String idToken = null; + response = request.execute(); + GenericData responseData = response.parseAs(GenericData.class); + idToken = (String) responseData.get("id_token"); + return idToken; + } + + public static HttpRequest buildIAPRequest(HttpRequest request) throws Exception { + // get service account credentials + ServiceAccountCredentials credentials = getCredentials(); + // get the base url of the request URL + String baseUrl = getBaseUrl(request.getUrl().toURL()); + String jwt = getSignedJWToken(credentials, baseUrl); + if (jwt == null) { + throw new Exception( + "Unable to create a signed jwt token for : " + + baseUrl + + "with issuer : " + + credentials.getClientEmail()); + } + + String idToken = getGoogleIdToken(jwt); + if (idToken == null) { + throw new Exception("Unable to retrieve open id token"); + } + + // Create an authorization header with bearer token + HttpHeaders httpHeaders = request.getHeaders().clone().setAuthorization("Bearer " + idToken); + + // create request with jwt authorization header + return httpTransport + .createRequestFactory() + .buildRequest(request.getRequestMethod(), request.getUrl(), request.getContent()) + .setHeaders(httpHeaders); + } + // [END generate_iap_request] +} diff --git a/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java b/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java new file mode 100644 index 00000000000..d8ac0a4e6e9 --- /dev/null +++ b/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java @@ -0,0 +1,162 @@ +/** + * Copyright 2017 Google Inc. + * + *

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.example.iap; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.auth0.jwt.interfaces.ECDSAKeyProvider; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpResponse; +import com.google.api.client.http.HttpStatusCodes; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.util.PemReader; +import com.google.api.client.util.PemReader.Section; +import java.io.IOException; +import java.io.StringReader; +import java.net.URL; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; + +/** Verify IAP authorization JWT token in incoming request. */ +public class VerifyIapRequestHeader { + + private static final String PUBLIC_KEY_VERIFICATION_URL = + "https://www.gstatic.com/iap/verify/public_key"; + private static final String IAP_ISSUER_URL = "https://cloud.google.com/iap"; + + private final Map keyCache = new HashMap<>(); + private final ObjectMapper mapper = new ObjectMapper(); + private final TypeReference> typeRef = + new TypeReference>() {}; + + private ECDSAKeyProvider keyProvider = + new ECDSAKeyProvider() { + @Override + public ECPublicKey getPublicKeyById(String kid) { + ECPublicKey key = keyCache.get(kid); + if (key != null) { + return key; + } + try { + HttpRequest request = + new NetHttpTransport() + .createRequestFactory() + .buildGetRequest(new GenericUrl(PUBLIC_KEY_VERIFICATION_URL)); + HttpResponse response = request.execute(); + if (response.getStatusCode() != HttpStatusCodes.STATUS_CODE_OK) { + return null; + } + Map keys = mapper.readValue(response.parseAsString(), typeRef); + for (Map.Entry keyData : keys.entrySet()) { + if (!keyData.getKey().equals(kid)) { + continue; + } + key = getKey(keyData.getValue()); + if (key != null) { + keyCache.putIfAbsent(kid, key); + } + } + + } catch (IOException e) { + // ignore exception + } + + return key; + } + + @Override + public ECPrivateKey getPrivateKey() { + // ignore : only required for signing requests + return null; + } + + @Override + public String getPrivateKeyId() { + // ignore : only required for signing requests + return null; + } + }; + + private static String getBaseUrl(URL url) throws Exception { + String urlFilePath = url.getFile(); + int pathDelim = urlFilePath.lastIndexOf('/'); + String path = (pathDelim > 0) ? urlFilePath.substring(0, pathDelim) : ""; + return (url.getProtocol() + "://" + url.getHost() + path).trim(); + } + + DecodedJWT verifyJWTToken(HttpRequest request) throws Exception { + // Check for iap jwt header in incoming request + String jwtToken = + request.getHeaders().getFirstHeaderStringValue("x-goog-authenticated-user-jwt"); + if (jwtToken == null) { + return null; + } + String baseUrl = getBaseUrl(request.getUrl().toURL()); + return verifyJWTToken(jwtToken, baseUrl); + } + + DecodedJWT verifyJWTToken(String jwtToken, String baseUrl) throws Exception { + Algorithm algorithm = Algorithm.ECDSA256(keyProvider); + + // Time constraints are automatically checked, use acceptLeeway to specify a leeway window + // The token was issued in a past date "iat" < TODAY + // The token hasn't expired yet "exp" > TODAY + JWTVerifier verifier = + JWT.require(algorithm).withAudience(baseUrl).withIssuer(IAP_ISSUER_URL).build(); + + DecodedJWT decodedJWT = verifier.verify(jwtToken); + + if (decodedJWT.getSubject() == null) { + throw new JWTVerificationException("Subject expected, not found"); + } + if (decodedJWT.getClaim("email") == null) { + throw new JWTVerificationException("Email expected, not found"); + } + return decodedJWT; + } + + private ECPublicKey getKey(String keyText) throws IOException { + StringReader reader = new StringReader(keyText); + Section section = PemReader.readFirstSectionAndClose(reader, "PUBLIC KEY"); + if (section == null) { + throw new IOException("Invalid data."); + } else { + byte[] bytes = section.getBase64DecodedBytes(); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes); + try { + KeyFactory kf = KeyFactory.getInstance("EC"); + PublicKey publicKey = kf.generatePublic(keySpec); + if (publicKey instanceof ECPublicKey) { + return (ECPublicKey) publicKey; + } + } catch (InvalidKeySpecException | NoSuchAlgorithmException var7) { + throw new IOException("Unexpected exception reading data", var7); + } + } + return null; + } +} From ee46851b47a46bd0ac72f69ce0d98c15a13a6945 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Sun, 28 May 2017 00:55:55 -0700 Subject: [PATCH 02/20] adding test --- .../iap/BuildAndVerifyIapRequestIT.java | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 iap/src/test/java/com/example/iap/BuildAndVerifyIapRequestIT.java diff --git a/iap/src/test/java/com/example/iap/BuildAndVerifyIapRequestIT.java b/iap/src/test/java/com/example/iap/BuildAndVerifyIapRequestIT.java new file mode 100644 index 00000000000..3638599a5dc --- /dev/null +++ b/iap/src/test/java/com/example/iap/BuildAndVerifyIapRequestIT.java @@ -0,0 +1,76 @@ +/** + * Copyright 2017 Google Inc. + * + * 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.example.iap; + +import static com.example.iap.BuildIapRequest.buildIAPRequest; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import com.auth0.jwt.interfaces.DecodedJWT; +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpResponse; +import com.google.api.client.http.HttpResponseException; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import org.apache.http.HttpStatus; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class BuildAndVerifyIapRequestIT { + + private String iapProtectedUrl = System.getenv("IAP_PROTECTED_URL"); + private HttpTransport httpTransport = new NetHttpTransport(); + private VerifyIapRequestHeader verifyIapRequestHeader = new VerifyIapRequestHeader(); + + @Before + public void setUp() { + assertNotNull(iapProtectedUrl); + } + + // Access an IAP protected url without signed jwt authorization header + @Test + public void accessIapProtectedResourceFailsWithoutJwtHeader() throws Exception { + HttpRequest request = + httpTransport.createRequestFactory().buildGetRequest(new GenericUrl(iapProtectedUrl)); + try { + request.execute(); + } catch (HttpResponseException e) { + assertEquals(e.getStatusCode(), HttpStatus.SC_UNAUTHORIZED); + } + } + + // Access an IAP protected url with a signed jwt authorization header, verify jwt token + @Test + public void testGenerateAndVerifyIapRequestIsSuccessful() throws Exception { + HttpRequest request = + httpTransport.createRequestFactory().buildGetRequest(new GenericUrl(iapProtectedUrl)); + HttpRequest iapRequest = buildIAPRequest(request); + HttpResponse response = iapRequest.execute(); + assertEquals(response.getStatusCode(), HttpStatus.SC_OK); + String headerWithtoken = response.parseAsString(); + String[] split = headerWithtoken.split(":"); + assertNotNull(split); + assertEquals(split.length, 2); + assertEquals(split[0].trim(), "x-goog-authenticated-user-jwt"); + DecodedJWT decodedJWT = verifyIapRequestHeader.verifyJWTToken(split[1].trim(), iapProtectedUrl); + assertNotNull(decodedJWT); + } +} From b491208d555c0b76f085fa22f9e0907d53e6c977 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Sun, 28 May 2017 00:58:36 -0700 Subject: [PATCH 03/20] cleanup --- appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml b/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml index a48cd61cf83..eec674e0766 100644 --- a/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml +++ b/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml @@ -12,7 +12,7 @@ limitations under the License. --> - ja-test-iap-2 + YOUR-PROJECT-ID alpha-001 true From dca4a022514189300439eb024290cdced7f34012 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Sun, 28 May 2017 01:01:31 -0700 Subject: [PATCH 04/20] license update --- appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml b/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml index eec674e0766..be18ac6ce52 100644 --- a/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml +++ b/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml @@ -1,6 +1,6 @@ + appengine-iap + + + 1.9.53 + + com.google.cloud appengine-doc-samples diff --git a/appengine/pom.xml b/appengine/pom.xml index 151a4d840d9..2950bd7651c 100644 --- a/appengine/pom.xml +++ b/appengine/pom.xml @@ -61,6 +61,7 @@ guestbook-objectify helloworld helloworld-new-plugins + iap images logs mailgun diff --git a/iap/pom.xml b/iap/pom.xml index 2fa49738965..4f01bf5fdde 100644 --- a/iap/pom.xml +++ b/iap/pom.xml @@ -15,20 +15,17 @@ limitations under the License. --> - - - 4.0.0 - jar - + + 4.0.0 + jar com.example - iap-samples - 1.0-SNAPSHOT + iap-samples + 1.0-SNAPSHOT - 1.8 - 1.8 + 1.8 + 1.8 UTF-8 - 9.4.3.v20170317 From 7a13ebc75f70d3a8113f1e4947a0d04b5da8f242 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Tue, 30 May 2017 11:35:05 -0700 Subject: [PATCH 08/20] using new tooling readme, pom.xml cleanup --- appengine/iap/README.md | 14 ++++++++++++-- appengine/iap/pom.xml | 8 ++------ .../src/main/webapp/WEB-INF/appengine-web.xml | 1 - iap/README.md | 19 +++++++++++-------- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/appengine/iap/README.md b/appengine/iap/README.md index 5f1b0c5e84c..39ca52ea4f7 100644 --- a/appengine/iap/README.md +++ b/appengine/iap/README.md @@ -6,16 +6,26 @@ Engine][ae-docs]. [iap-docs]: https://cloud.google.com/iap/docs/ [ae-docs]: https://cloud.google.com/appengine/docs/java/ +## Setup + +Install the [Google Cloud SDK](https://cloud.google.com/sdk/) and run: +``` + gcloud init +``` +If this is your first time creating an App engine application: +``` + gcloud app create +``` + ## Running locally This application depends on being enabled behind an IAP, so this program should not be run locally. ## Deploying -- Update [appengine-web.xml](src/main/test/app/src/main/webapp/WEB-INF/appengine-web.xml) with your project-id - Deploy the application to the project ``` - mvn clean appengine:update + mvn clean appengine:deploy ``` - [Enable](https://cloud.google.com/iap/docs/app-engine-quickstart) Identity-Aware Proxy on the App Engine app. - Add the email account you'll be running the test as to the Identity-Aware Proxy access list for the project. diff --git a/appengine/iap/pom.xml b/appengine/iap/pom.xml index 0f8b1bb321f..15b6cd930d8 100644 --- a/appengine/iap/pom.xml +++ b/appengine/iap/pom.xml @@ -20,10 +20,6 @@ Copyright 2017 Google Inc. com.example.appengine appengine-iap - - 1.9.53 - - com.google.cloud appengine-doc-samples @@ -52,9 +48,9 @@ Copyright 2017 Google Inc. - com.google.appengine + com.google.cloud.tools appengine-maven-plugin - ${appengine.sdk.version} + 1.3.1 diff --git a/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml b/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml index be18ac6ce52..153fcd0c49f 100644 --- a/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml +++ b/appengine/iap/src/main/webapp/WEB-INF/appengine-web.xml @@ -12,7 +12,6 @@ limitations under the License. --> - YOUR-PROJECT-ID alpha-001 true diff --git a/iap/README.md b/iap/README.md index 0c8192b8fdd..d8222e2c512 100644 --- a/iap/README.md +++ b/iap/README.md @@ -3,31 +3,33 @@ Cloud Identity-Aware Proxy (Cloud IAP) lets you manage access to applications ru ## Setup - A Google Cloud project with billing enabled -- [Create an App engine service account](https://cloud.google.com/docs/authentication#getting_credentials_for_server-centric_flow) and download the credentials file as JSON. +- A service account with private key credentials is required to create signed bearer tokens. + - [Create an App engine service account](https://cloud.google.com/docs/authentication#getting_credentials_for_server-centric_flow) and download the credentials file as JSON. + - Set the environment variable `GOOGLE_APPLICATION_CREDENTIALS` to point to the service account credentials file. - Install the [Google Cloud SDK](https://cloud.google.com/sdk/) and run: ``` gcloud init +``` + If this is your first time creating an App engine application: +``` gcloud app create ``` ## Description - [BuildIapRequest.java](src/main/java/com/example/iap/BuildIapRequest.java) demonstrates how to set the -`Authorization : Bearer` header to authorize access to an IAP protected URL. +`Authorization : Bearer` header with a signed JWT token to authorize access to an IAP protected URL. - [VerifyIapRequestHeader.java](src/main/java/com/example/iap/VerifyIapRequestHeader.java) demonstrates how to verify the JWT token in an incoming request to an IAP protected resource. ## Testing - Deploy the [demo app engine application](../appengine/iap/README.md). This application will return the JWT token to an authorized incoming request. It will be used to test both the authorization of an incoming request to an IAP protected resource and the JWT token returned from IAP. - - Update [appengine-web.xml](../appengine/src/main/webapp/WEB-INF/appengine-web.xml) - with your project-id - Deploy the application to the project ``` - mvn clean appengine:update + mvn clean appengine:deploy ``` - - [Enable](https://cloud.google.com/iap/docs/app-engine-quickstart) Identity-Aware Proxy on the App Engine app. -- Set the environment variable `GOOGLE_APPLICATION_CREDENTIALS` to point to the service account credentials file -- Add the service account email you'll be running the test as to the Identity-Aware Proxy access list for the project. +- [Enable](https://cloud.google.com/iap/docs/app-engine-quickstart) Identity-Aware Proxy on the App Engine app. +- Add the service account email to the Identity-Aware Proxy access list for the project. - Set the environment variable `IAP_PROTECTED_URL` to point to `https://your-project-id.appspot.com` - Run the integration test: ``` @@ -37,3 +39,4 @@ It will be used to test both the authorization of an incoming request to an IAP ## References [JWT library for Java](https://github.com/auth0/java-jwt) [Cloud IAP docs](https://cloud.google.com/iap/docs/) +[Service account credentials](https://cloud.google.com/docs/authentication#getting_credentials_for_server-centric_flow) From ebd29dccf01e666cf4508f92d06eac3f239b2832 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Tue, 30 May 2017 12:07:51 -0700 Subject: [PATCH 09/20] adding doc tags --- iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java b/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java index d8ac0a4e6e9..9e21fa79547 100644 --- a/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java +++ b/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java @@ -43,7 +43,7 @@ /** Verify IAP authorization JWT token in incoming request. */ public class VerifyIapRequestHeader { - + // [START verify_iap_request] private static final String PUBLIC_KEY_VERIFICATION_URL = "https://www.gstatic.com/iap/verify/public_key"; private static final String IAP_ISSUER_URL = "https://cloud.google.com/iap"; @@ -159,4 +159,5 @@ private ECPublicKey getKey(String keyText) throws IOException { } return null; } + // [END verify_iap_request] } From 65e9a2433f9ff33a8bbdae02ff4de52b50716c70 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Wed, 31 May 2017 09:25:02 -0700 Subject: [PATCH 10/20] PubSub getting started : initial commit --- pubsub/cloud-client/README.md | 51 +++++--- pubsub/cloud-client/pom.xml | 41 +++--- ...artSample.java => CreateTopicExample.java} | 11 +- .../java/com/example/pubsub/QuickStartIT.java | 123 ++++++++++++++++++ .../example/pubsub/QuickstartSampleIT.java | 78 ----------- 5 files changed, 185 insertions(+), 119 deletions(-) rename pubsub/cloud-client/src/main/java/com/example/pubsub/{QuickstartSample.java => CreateTopicExample.java} (87%) create mode 100644 pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java delete mode 100644 pubsub/cloud-client/src/test/java/com/example/pubsub/QuickstartSampleIT.java diff --git a/pubsub/cloud-client/README.md b/pubsub/cloud-client/README.md index b1c444506e5..6d773adf363 100644 --- a/pubsub/cloud-client/README.md +++ b/pubsub/cloud-client/README.md @@ -14,24 +14,41 @@ For more samples, see the samples in ## Quickstart #### Setup -- Install [Maven](http://maven.apache.org/)

+- Install [Maven](http://maven.apache.org/) - Install the [Google Cloud SDK](https://cloud.google.com/sdk/) and run : - - - gcloud config set project [YOUR PROJECT ID] - - +``` + gcloud config set project [YOUR PROJECT ID] +``` +- [Enable](https://console.cloud.google.com/apis/api/pubsub.googleapis.com/overview) Pub/Sub API - Build your project with: - - - mvn clean package -DskipTests +``` + mvn clean package -DskipTests +``` + +#### Create a new topic +``` + mvn exec:java -Dexec.mainClass=com.example.pubsub.CreateTopicExample -Dexec.args=my-topic-id +``` + +#### Publish messages +``` + mvn exec:java -Dexec.mainClass=com.example.pubsub.PublisherExample -Dexec.args=my-topic-id +``` +Publishes 10 messages to the topic `my-topic-id`. + +#### Create a subscription +``` + mvn exec:java -Dexec.mainClass=com.example.pubsub.CreatePullSubscriptionExample -Dexec.args="my-topic-id my-subscription-id" +``` + +#### Receive messages +``` + mvn exec:java -Dexec.mainClass=com.example.pubsub.SubscriberExample -Dexec.args=my-subscription-id +``` +Subscriber will continue to listen on the topic for 5 minutes and print out message id and data as messages are received. #### Testing - -Run the tests with Maven. - - mvn clean verify - -#### Creating a new topic (using the quickstart sample) - - mvn exec:java -Dexec.mainClass=com.example.pubsub.QuickstartSample +Run the test with Maven. +``` + mvn clean -Dtest=com.example.pubsub.QuickStartIT verify +``` diff --git a/pubsub/cloud-client/pom.xml b/pubsub/cloud-client/pom.xml index 83c6b0f0d6b..6fb289832f5 100644 --- a/pubsub/cloud-client/pom.xml +++ b/pubsub/cloud-client/pom.xml @@ -21,27 +21,27 @@ - doc-samples - com.google.cloud - 1.0.0 - ../.. - + doc-samples + com.google.cloud + 1.0.0 + ../.. + - - 1.8 - 1.8 - UTF-8 - 0.17.2-alpha - + + 1.8 + 1.8 + UTF-8 + 0.18.0-alpha + - - - com.google.cloud - google-cloud-pubsub - ${pubsub.version} - + + + com.google.cloud + google-cloud-pubsub + ${pubsub.version} + - + junit junit @@ -54,5 +54,10 @@ 0.32 test + + com.google.guava + guava + 20.0 + diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/QuickstartSample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/CreateTopicExample.java similarity index 87% rename from pubsub/cloud-client/src/main/java/com/example/pubsub/QuickstartSample.java rename to pubsub/cloud-client/src/main/java/com/example/pubsub/CreateTopicExample.java index d37c3cdafd6..d2dcb25b730 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/QuickstartSample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/CreateTopicExample.java @@ -16,22 +16,21 @@ package com.example.pubsub; -// [START pubsub_quickstart] +// [START pubsub_quickstart_create_topic] // Imports the Google Cloud client library - import com.google.cloud.ServiceOptions; import com.google.cloud.pubsub.spi.v1.TopicAdminClient; import com.google.pubsub.v1.TopicName; -public class QuickstartSample { +public class CreateTopicExample { public static void main(String... args) throws Exception { // Your Google Cloud Platform project ID String projectId = ServiceOptions.getDefaultProjectId(); - // Your topic ID - String topicId = "my-new-topic"; + // Your topic ID, eg. "my-topic-id" + String topicId = args[0]; // Create a new topic TopicName topic = TopicName.create(projectId, topicId); @@ -42,4 +41,4 @@ public static void main(String... args) throws Exception { System.out.printf("Topic %s:%s created.\n", topic.getProject(), topic.getTopic()); } } -// [END pubsub_quickstart] +// [END pubsub_quickstart_create_topic] diff --git a/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java b/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java new file mode 100644 index 00000000000..a09405a08e7 --- /dev/null +++ b/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java @@ -0,0 +1,123 @@ +/* + Copyright 2016, Google, Inc. + + 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.example.pubsub; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.ServiceOptions; +import com.google.cloud.pubsub.spi.v1.SubscriptionAdminClient; +import com.google.cloud.pubsub.spi.v1.TopicAdminClient; +import com.google.pubsub.v1.SubscriptionName; +import com.google.pubsub.v1.TopicName; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.List; + +/** Tests for quickstart sample. */ +@RunWith(JUnit4.class) +@SuppressWarnings("checkstyle:abbreviationaswordinname") +public class QuickStartIT { + + private ByteArrayOutputStream bout; + private PrintStream out; + + private String projectId = ServiceOptions.getDefaultProjectId(); + private String topicId = formatForTest("my-topic-id"); + private String subscriptionId = formatForTest("my-subscription-id"); + + @Rule public Timeout globalTimeout = Timeout.seconds(300); // 5 minute timeout + + @Before + public void setUp() { + bout = new ByteArrayOutputStream(); + out = new PrintStream(bout); + System.setOut(out); + try { + deleteTestSubscription(); + deleteTestTopic(); + } catch (Exception e) { + // topic, subscription may not yet exist + } + } + + @After + public void tearDown() throws Exception { + System.setOut(null); + deleteTestSubscription(); + deleteTestTopic(); + } + + @Test + public void testQuickstart() throws Exception { + //create a topic + CreateTopicExample.main(topicId); + String got = bout.toString(); + assertThat(got).contains(topicId + " created."); + + // publish messages + List published = PublisherExample.publishMessages(topicId); + assertThat(published).hasSize(5); + + // create a subscriber + CreatePullSubscriptionExample.main(topicId, subscriptionId); + got = bout.toString(); + assertThat(got).contains(subscriptionId + " created."); + + SubscriberExample subscriberExample = new SubscriberExample(subscriptionId); + // receive messages + Thread subscriberThread = new Thread(subscriberExample); + subscriberThread.start(); + + List received; + while ((received = subscriberExample.getReceivedMessages()).size() < 5) { + Thread.sleep(1000); + } + assertThat(received).containsAllIn(published); + subscriberExample.stopSubscriber(); + subscriberThread.join(); + } + + private String formatForTest(String name) { + return name + "-" + java.util.UUID.randomUUID().toString(); + } + + private void deleteTestTopic() throws Exception { + try (TopicAdminClient topicAdminClient = TopicAdminClient.create()) { + topicAdminClient.deleteTopic(TopicName.create(projectId, topicId)); + } catch (IOException e) { + System.err.println("Error deleting topic " + e.getMessage()); + } + } + + private void deleteTestSubscription() throws Exception { + try (SubscriptionAdminClient subscriptionAdminClient = SubscriptionAdminClient.create()) { + subscriptionAdminClient.deleteSubscription( + SubscriptionName.create(projectId, subscriptionId)); + } catch (IOException e) { + System.err.println("Error deleting subscription " + e.getMessage()); + } + } +} diff --git a/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickstartSampleIT.java b/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickstartSampleIT.java deleted file mode 100644 index bd5a781ccf3..00000000000 --- a/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickstartSampleIT.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - Copyright 2016, Google, Inc. - - 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.example.pubsub; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.cloud.ServiceOptions; -import com.google.cloud.pubsub.spi.v1.TopicAdminClient; -import com.google.pubsub.v1.TopicName; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.PrintStream; - -/** - * Tests for quickstart sample. - */ -@RunWith(JUnit4.class) -@SuppressWarnings("checkstyle:abbreviationaswordinname") -public class QuickstartSampleIT { - - private ByteArrayOutputStream bout; - private PrintStream out; - - private void deleteTestTopic() throws Exception { - try (TopicAdminClient topicAdminClient = TopicAdminClient.create()) { - topicAdminClient.deleteTopic( - TopicName.create(ServiceOptions.getDefaultProjectId(), "my-new-topic")); - } catch (IOException e) { - System.err.println("Error deleting topic " + e.getMessage()); - } - } - - @Before - public void setUp() { - bout = new ByteArrayOutputStream(); - out = new PrintStream(bout); - System.setOut(out); - try { - deleteTestTopic(); - } catch (Exception e) { - //empty catch block - } - } - - @After - public void tearDown() throws Exception { - System.setOut(null); - deleteTestTopic(); - } - - @Test - public void testQuickstart() throws Exception { - QuickstartSample.main(); - String got = bout.toString(); - assertThat(got).contains("my-new-topic created."); - } -} From f3176bc6e392a72584d4d69811826871cb8cf6ce Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Wed, 31 May 2017 09:25:46 -0700 Subject: [PATCH 11/20] getting started guide files --- .../pubsub/CreatePullSubscriptionExample.java | 56 ++++++++++ .../com/example/pubsub/PublisherExample.java | 87 +++++++++++++++ .../com/example/pubsub/SubscriberExample.java | 101 ++++++++++++++++++ 3 files changed, 244 insertions(+) create mode 100644 pubsub/cloud-client/src/main/java/com/example/pubsub/CreatePullSubscriptionExample.java create mode 100644 pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java create mode 100644 pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/CreatePullSubscriptionExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/CreatePullSubscriptionExample.java new file mode 100644 index 00000000000..2b3838c79a0 --- /dev/null +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/CreatePullSubscriptionExample.java @@ -0,0 +1,56 @@ +/* + Copyright 2017, Google, Inc. + + 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.example.pubsub; + +// [START pubsub_quickstart_create_subscription] +import com.google.cloud.ServiceOptions; +import com.google.cloud.pubsub.spi.v1.SubscriptionAdminClient; +import com.google.pubsub.v1.PushConfig; +import com.google.pubsub.v1.Subscription; +import com.google.pubsub.v1.SubscriptionName; +import com.google.pubsub.v1.TopicName; + +public class CreatePullSubscriptionExample { + + public static void main(String... args) throws Exception { + + // Your Google Cloud Platform project ID + String projectId = ServiceOptions.getDefaultProjectId(); + + // Your topic ID, eg. "my-topic-id" + String topicId = args[0]; + + // Your subscription ID eg. "my-subscription-id" + String subscriptionId = args[1]; + + TopicName topicName = TopicName.create(projectId, topicId); + + // Create a new subscription + SubscriptionName subscriptionName = SubscriptionName.create(projectId, subscriptionId); + try (SubscriptionAdminClient subscriptionAdminClient = SubscriptionAdminClient.create()) { + // create a pull subscription with default acknowledgement deadline (= 10 seconds) + Subscription subscription = + subscriptionAdminClient.createSubscription( + subscriptionName, topicName, PushConfig.getDefaultInstance(), 0); + } + + System.out.printf( + "Subscription %s:%s created.\n", + subscriptionName.getProject(), subscriptionName.getSubscription()); + } +} +// [END pubsub_quickstart_create_subscription] diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java new file mode 100644 index 00000000000..f8ed186f012 --- /dev/null +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java @@ -0,0 +1,87 @@ +/* + * Copyright 2017 Google Inc. + * + * 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.example.pubsub; +// [START pubsub_quickstart_publisher] +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; +import com.google.cloud.ServiceOptions; +import com.google.cloud.pubsub.spi.v1.Publisher; +import com.google.protobuf.ByteString; +import com.google.pubsub.v1.PubsubMessage; +import com.google.pubsub.v1.TopicName; + +import java.util.ArrayList; +import java.util.List; + +public class PublisherExample { + + // use the default project id + private static final String PROJECT_ID = ServiceOptions.getDefaultProjectId(); + + private static final int MESSAGE_COUNT = 5; + + //publish messages asynchronously one at a time. + static List publishMessages(String topicId) throws Exception { + List messageIds; + List> messageIdFutures = new ArrayList<>(); + TopicName topicName = TopicName.create(PROJECT_ID, topicId); + Publisher publisher = null; + try { + // Create a publisher instance with default settings bound to the topic + publisher = Publisher.defaultBuilder(topicName).build(); + List messages = getMessages(); + + // schedule publishing one message at a time : messages get automatically batched + for (String message : messages) { + // convert message to bytes + ByteString data = ByteString.copyFromUtf8(message); + PubsubMessage pubsubMessage = PubsubMessage.newBuilder().setData(data).build(); + + // Once published, returns a server-assigned message id (unique within the topic) + ApiFuture messageIdFuture = publisher.publish(pubsubMessage); + messageIdFutures.add(messageIdFuture); + } + } finally { + // wait on any pending publish requests. + messageIds = ApiFutures.allAsList(messageIdFutures).get(); + + for (String messageId : messageIds) { + System.out.println("published with message ID: " + messageId); + } + + if (publisher != null) { + // When finished with the publisher, shutdown to free up resources. + publisher.shutdown(); + } + } + return messageIds; + } + + private static List getMessages() { + List messages = new ArrayList<>(); + for (int i = 0; i < MESSAGE_COUNT; i++) { + messages.add("message-" + String.valueOf(i)); + } + return messages; + } + + public static void main(String... args) throws Exception { + // topic id, eg. "my-topic-id" + String topicId = args[0]; + publishMessages(topicId); + } +} +// [END pubsub_quickstart_quickstart] diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java new file mode 100644 index 00000000000..aa3ee815c26 --- /dev/null +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java @@ -0,0 +1,101 @@ +/* + * Copyright 2017 Google Inc. + * + * 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.example.pubsub; + +// [START pubsub_quickstart_subscriber] +import com.google.cloud.ServiceOptions; +import com.google.cloud.pubsub.spi.v1.AckReplyConsumer; +import com.google.cloud.pubsub.spi.v1.MessageReceiver; +import com.google.cloud.pubsub.spi.v1.Subscriber; +import com.google.common.collect.ImmutableList; +import com.google.pubsub.v1.PubsubMessage; +import com.google.pubsub.v1.SubscriptionName; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingDeque; + +public class SubscriberExample implements Runnable { + // use the default project id + private static final String PROJECT_ID = ServiceOptions.getDefaultProjectId(); + + private final BlockingQueue messages = new LinkedBlockingDeque<>(); + + private final List receivedMessageIds = new ArrayList<>(); + + private final String subscriptionId; + + private volatile boolean listen = true; + + public SubscriberExample(String subscriptionId) { + this.subscriptionId = subscriptionId; + } + + @Override + public void run() { + MessageReceiver receiver = + new MessageReceiver() { + @Override + public void receiveMessage(PubsubMessage message, AckReplyConsumer consumer) { + messages.offer(message); + consumer.ack(); + } + }; + SubscriptionName subscriptionName = SubscriptionName.create(PROJECT_ID, subscriptionId); + Subscriber subscriber = null; + try { + // create a subscriber bound to the asynchronous message receiver + subscriber = Subscriber.defaultBuilder(subscriptionName, receiver).build(); + subscriber.startAsync().awaitRunning(); + // continue to wait on received messages, Ctrl-C to exit + while (listen) { + // block on receiving a message + PubsubMessage message = messages.take(); + System.out.println("Message Id: " + message.getMessageId()); + System.out.println("Data: " + message.getData().toStringUtf8()); + receivedMessageIds.add(message.getMessageId()); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + if (subscriber != null) { + subscriber.stopAsync(); + } + } + } + + void stopSubscriber() { + listen = false; + } + + List getReceivedMessages() { + return ImmutableList.copyOf(receivedMessageIds); + } + + public static void main(String... args) throws Exception { + // set subscriber id, eg. my-subscriber-id + String subscriberId = args[0]; + SubscriberExample subscriber = new SubscriberExample(subscriberId); + Thread t = new Thread(subscriber); + t.start(); + // Stop subscriber after 5 minutes of listening + Thread.sleep(5 * 60000); + subscriber.stopSubscriber(); + t.join(); + } +} +// [END pubsub_quickstart_subscriber] From e53c18217aef6fce5be6c755706f3e999edb6f46 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Wed, 31 May 2017 09:34:40 -0700 Subject: [PATCH 12/20] formatting fixes --- pubsub/cloud-client/pom.xml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pubsub/cloud-client/pom.xml b/pubsub/cloud-client/pom.xml index 6fb289832f5..11d8bf1f654 100644 --- a/pubsub/cloud-client/pom.xml +++ b/pubsub/cloud-client/pom.xml @@ -21,27 +21,27 @@ - doc-samples - com.google.cloud - 1.0.0 - ../.. - + doc-samples + com.google.cloud + 1.0.0 + ../.. + - - 1.8 - 1.8 - UTF-8 - 0.18.0-alpha - + + 1.8 + 1.8 + UTF-8 + 0.18.0-alpha + - - - com.google.cloud - google-cloud-pubsub - ${pubsub.version} - + + + com.google.cloud + google-cloud-pubsub + ${pubsub.version} + - + junit junit From 011c88fa17faf5e0ac53d5bd3296cec651842c90 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Wed, 31 May 2017 11:31:02 -0700 Subject: [PATCH 13/20] create subscription before publishing a message --- pubsub/cloud-client/README.md | 13 +++++++------ .../test/java/com/example/pubsub/QuickStartIT.java | 12 ++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/pubsub/cloud-client/README.md b/pubsub/cloud-client/README.md index 6d773adf363..7ac33119138 100644 --- a/pubsub/cloud-client/README.md +++ b/pubsub/cloud-client/README.md @@ -15,9 +15,10 @@ For more samples, see the samples in #### Setup - Install [Maven](http://maven.apache.org/) -- Install the [Google Cloud SDK](https://cloud.google.com/sdk/) and run : +- Install the [Google Cloud SDK](https://cloud.google.com/sdk/) and authenticate : ``` gcloud config set project [YOUR PROJECT ID] + gcloud auth application-default login ``` - [Enable](https://console.cloud.google.com/apis/api/pubsub.googleapis.com/overview) Pub/Sub API - Build your project with: @@ -30,16 +31,16 @@ For more samples, see the samples in mvn exec:java -Dexec.mainClass=com.example.pubsub.CreateTopicExample -Dexec.args=my-topic-id ``` -#### Publish messages +#### Create a subscription ``` - mvn exec:java -Dexec.mainClass=com.example.pubsub.PublisherExample -Dexec.args=my-topic-id + mvn exec:java -Dexec.mainClass=com.example.pubsub.CreatePullSubscriptionExample -Dexec.args="my-topic-id my-subscription-id" ``` -Publishes 10 messages to the topic `my-topic-id`. -#### Create a subscription +#### Publish messages ``` - mvn exec:java -Dexec.mainClass=com.example.pubsub.CreatePullSubscriptionExample -Dexec.args="my-topic-id my-subscription-id" + mvn exec:java -Dexec.mainClass=com.example.pubsub.PublisherExample -Dexec.args=my-topic-id ``` +Publishes 5 messages to the topic `my-topic-id`. #### Receive messages ``` diff --git a/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java b/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java index a09405a08e7..1dfc7bcdb39 100644 --- a/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java +++ b/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java @@ -72,20 +72,20 @@ public void tearDown() throws Exception { @Test public void testQuickstart() throws Exception { - //create a topic + // create a topic CreateTopicExample.main(topicId); String got = bout.toString(); assertThat(got).contains(topicId + " created."); - // publish messages - List published = PublisherExample.publishMessages(topicId); - assertThat(published).hasSize(5); - // create a subscriber CreatePullSubscriptionExample.main(topicId, subscriptionId); got = bout.toString(); assertThat(got).contains(subscriptionId + " created."); + // publish messages + List published = PublisherExample.publishMessages(topicId); + assertThat(published).hasSize(5); + SubscriberExample subscriberExample = new SubscriberExample(subscriptionId); // receive messages Thread subscriberThread = new Thread(subscriberExample); @@ -95,9 +95,9 @@ public void testQuickstart() throws Exception { while ((received = subscriberExample.getReceivedMessages()).size() < 5) { Thread.sleep(1000); } + assertThat(received).containsAllIn(published); subscriberExample.stopSubscriber(); - subscriberThread.join(); } private String formatForTest(String name) { From 9404a36efdc5d8e1ae7073a9da1973023710cc8a Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Mon, 19 Jun 2017 13:35:14 -0700 Subject: [PATCH 14/20] simplifying publisher, subscriber snippets --- .../com/example/pubsub/PublisherExample.java | 57 +++++++---------- .../com/example/pubsub/SubscriberExample.java | 62 ++++++------------- .../java/com/example/pubsub/QuickStartIT.java | 54 ++++++++++++---- 3 files changed, 80 insertions(+), 93 deletions(-) diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java index f8ed186f012..556ec496578 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java @@ -28,60 +28,45 @@ public class PublisherExample { + static final int MESSAGE_COUNT = 5; + // use the default project id private static final String PROJECT_ID = ServiceOptions.getDefaultProjectId(); - private static final int MESSAGE_COUNT = 5; + //publish message asynchronously one at a time. + private static ApiFuture publishMessage(Publisher publisher, String message) throws Exception { + // schedule publishing : messages get automatically batched + // convert message to bytes + ByteString data = ByteString.copyFromUtf8(message); + PubsubMessage pubsubMessage = PubsubMessage.newBuilder().setData(data).build(); + return publisher.publish(pubsubMessage); + } - //publish messages asynchronously one at a time. - static List publishMessages(String topicId) throws Exception { - List messageIds; - List> messageIdFutures = new ArrayList<>(); + public static void main(String... args) throws Exception { + // topic id, eg. "my-topic-id" + String topicId = args[0]; TopicName topicName = TopicName.create(PROJECT_ID, topicId); Publisher publisher = null; + List> apiFutures = new ArrayList<>(); try { // Create a publisher instance with default settings bound to the topic publisher = Publisher.defaultBuilder(topicName).build(); - List messages = getMessages(); - - // schedule publishing one message at a time : messages get automatically batched - for (String message : messages) { - // convert message to bytes - ByteString data = ByteString.copyFromUtf8(message); - PubsubMessage pubsubMessage = PubsubMessage.newBuilder().setData(data).build(); - - // Once published, returns a server-assigned message id (unique within the topic) - ApiFuture messageIdFuture = publisher.publish(pubsubMessage); - messageIdFutures.add(messageIdFuture); + for (int i = 0; i < MESSAGE_COUNT; i++) { + String message = "message-" + i; + ApiFuture messageId = publishMessage(publisher, message); + apiFutures.add(messageId); } } finally { - // wait on any pending publish requests. - messageIds = ApiFutures.allAsList(messageIdFutures).get(); - + // Once published, returns server-assigned message ids (unique within the topic) + List messageIds = ApiFutures.allAsList(apiFutures).get(); for (String messageId : messageIds) { - System.out.println("published with message ID: " + messageId); + System.out.println(messageId); } - if (publisher != null) { // When finished with the publisher, shutdown to free up resources. publisher.shutdown(); } } - return messageIds; - } - - private static List getMessages() { - List messages = new ArrayList<>(); - for (int i = 0; i < MESSAGE_COUNT; i++) { - messages.add("message-" + String.valueOf(i)); - } - return messages; - } - - public static void main(String... args) throws Exception { - // topic id, eg. "my-topic-id" - String topicId = args[0]; - publishMessages(topicId); } } // [END pubsub_quickstart_quickstart] diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java index aa3ee815c26..8662693b427 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java @@ -29,73 +29,47 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; -public class SubscriberExample implements Runnable { +public class SubscriberExample { + // use the default project id private static final String PROJECT_ID = ServiceOptions.getDefaultProjectId(); - private final BlockingQueue messages = new LinkedBlockingDeque<>(); + private static final BlockingQueue messages = new LinkedBlockingDeque<>(); private final List receivedMessageIds = new ArrayList<>(); - private final String subscriptionId; - - private volatile boolean listen = true; + static class MessageReceiverExample implements MessageReceiver { + @Override + public void receiveMessage(PubsubMessage message, AckReplyConsumer consumer) { + messages.offer(message); + consumer.ack(); + } + } - public SubscriberExample(String subscriptionId) { - this.subscriptionId = subscriptionId; + List getReceivedMessages() { + return ImmutableList.copyOf(receivedMessageIds); } - @Override - public void run() { - MessageReceiver receiver = - new MessageReceiver() { - @Override - public void receiveMessage(PubsubMessage message, AckReplyConsumer consumer) { - messages.offer(message); - consumer.ack(); - } - }; + public static void main(String... args) throws Exception { + // set subscriber id, eg. my-subscriber-id + String subscriptionId = args[0]; SubscriptionName subscriptionName = SubscriptionName.create(PROJECT_ID, subscriptionId); Subscriber subscriber = null; try { // create a subscriber bound to the asynchronous message receiver - subscriber = Subscriber.defaultBuilder(subscriptionName, receiver).build(); + subscriber = Subscriber.defaultBuilder(subscriptionName, new MessageReceiverExample()).build(); subscriber.startAsync().awaitRunning(); - // continue to wait on received messages, Ctrl-C to exit - while (listen) { - // block on receiving a message + // Continue to listen to messages + while (true) { PubsubMessage message = messages.take(); System.out.println("Message Id: " + message.getMessageId()); System.out.println("Data: " + message.getData().toStringUtf8()); - receivedMessageIds.add(message.getMessageId()); } - } catch (InterruptedException e) { - throw new RuntimeException(e); } finally { if (subscriber != null) { subscriber.stopAsync(); } } } - - void stopSubscriber() { - listen = false; - } - - List getReceivedMessages() { - return ImmutableList.copyOf(receivedMessageIds); - } - - public static void main(String... args) throws Exception { - // set subscriber id, eg. my-subscriber-id - String subscriberId = args[0]; - SubscriberExample subscriber = new SubscriberExample(subscriberId); - Thread t = new Thread(subscriber); - t.start(); - // Stop subscriber after 5 minutes of listening - Thread.sleep(5 * 60000); - subscriber.stopSubscriber(); - t.join(); - } } // [END pubsub_quickstart_subscriber] diff --git a/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java b/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java index 1dfc7bcdb39..fefd5be0fb6 100644 --- a/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java +++ b/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java @@ -23,6 +23,10 @@ import com.google.cloud.pubsub.spi.v1.TopicAdminClient; import com.google.pubsub.v1.SubscriptionName; import com.google.pubsub.v1.TopicName; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -42,18 +46,35 @@ public class QuickStartIT { private ByteArrayOutputStream bout; - private PrintStream out; private String projectId = ServiceOptions.getDefaultProjectId(); private String topicId = formatForTest("my-topic-id"); private String subscriptionId = formatForTest("my-subscription-id"); + class SubscriberRunnable implements Runnable { + + private String subscriptionId; + + SubscriberRunnable(String subscriptionId) { + this.subscriptionId = subscriptionId; + } + + @Override + public void run() { + try { + SubscriberExample.main(subscriptionId); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + @Rule public Timeout globalTimeout = Timeout.seconds(300); // 5 minute timeout @Before public void setUp() { bout = new ByteArrayOutputStream(); - out = new PrintStream(bout); + PrintStream out = new PrintStream(bout); System.setOut(out); try { deleteTestSubscription(); @@ -82,22 +103,29 @@ public void testQuickstart() throws Exception { got = bout.toString(); assertThat(got).contains(subscriptionId + " created."); + bout.reset(); // publish messages - List published = PublisherExample.publishMessages(topicId); - assertThat(published).hasSize(5); + PublisherExample.main(topicId); + String[] messageIds = bout.toString().split("\n"); + assertThat(messageIds).hasLength(PublisherExample.MESSAGE_COUNT); - SubscriberExample subscriberExample = new SubscriberExample(subscriptionId); + bout.reset(); // receive messages - Thread subscriberThread = new Thread(subscriberExample); + Thread subscriberThread = new Thread(new SubscriberRunnable(subscriptionId)); subscriberThread.start(); - - List received; - while ((received = subscriberExample.getReceivedMessages()).size() < 5) { - Thread.sleep(1000); + Set expectedMessageIds = new HashSet<>(); + List receivedMessageIds = new ArrayList<>(); + expectedMessageIds.addAll(Arrays.asList(messageIds)); + while (!expectedMessageIds.isEmpty()) { + for (String expectedId : expectedMessageIds) { + if (bout.toString().contains(expectedId)) { + receivedMessageIds.add(expectedId); + } + } + expectedMessageIds.removeAll(receivedMessageIds); } - - assertThat(received).containsAllIn(published); - subscriberExample.stopSubscriber(); + subscriberThread.interrupt(); + assertThat(expectedMessageIds).isEmpty(); } private String formatForTest(String name) { From e2a3302daad3504d74c38428a3579f3f226723f9 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Mon, 19 Jun 2017 14:17:10 -0700 Subject: [PATCH 15/20] updates --- .../com/example/pubsub/PublisherExample.java | 15 ++++++++------- .../com/example/pubsub/SubscriberExample.java | 5 ++++- .../java/com/example/pubsub/QuickStartIT.java | 16 +++++++++------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java index 556ec496578..14cd327cd3f 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java @@ -15,6 +15,7 @@ */ package com.example.pubsub; // [START pubsub_quickstart_publisher] + import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; import com.google.cloud.ServiceOptions; @@ -33,13 +34,13 @@ public class PublisherExample { // use the default project id private static final String PROJECT_ID = ServiceOptions.getDefaultProjectId(); - //publish message asynchronously one at a time. - private static ApiFuture publishMessage(Publisher publisher, String message) throws Exception { - // schedule publishing : messages get automatically batched - // convert message to bytes - ByteString data = ByteString.copyFromUtf8(message); - PubsubMessage pubsubMessage = PubsubMessage.newBuilder().setData(data).build(); - return publisher.publish(pubsubMessage); + //schedule a message to be published, messages are automatically batched + private static ApiFuture publishMessage(Publisher publisher, String message) + throws Exception { + // convert message to bytes + ByteString data = ByteString.copyFromUtf8(message); + PubsubMessage pubsubMessage = PubsubMessage.newBuilder().setData(data).build(); + return publisher.publish(pubsubMessage); } public static void main(String... args) throws Exception { diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java index 8662693b427..d11e88622c5 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java @@ -16,6 +16,7 @@ package com.example.pubsub; // [START pubsub_quickstart_subscriber] + import com.google.cloud.ServiceOptions; import com.google.cloud.pubsub.spi.v1.AckReplyConsumer; import com.google.cloud.pubsub.spi.v1.MessageReceiver; @@ -39,6 +40,7 @@ public class SubscriberExample { private final List receivedMessageIds = new ArrayList<>(); static class MessageReceiverExample implements MessageReceiver { + @Override public void receiveMessage(PubsubMessage message, AckReplyConsumer consumer) { messages.offer(message); @@ -57,7 +59,8 @@ public static void main(String... args) throws Exception { Subscriber subscriber = null; try { // create a subscriber bound to the asynchronous message receiver - subscriber = Subscriber.defaultBuilder(subscriptionName, new MessageReceiverExample()).build(); + subscriber = Subscriber.defaultBuilder(subscriptionName, new MessageReceiverExample()) + .build(); subscriber.startAsync().awaitRunning(); // Continue to listen to messages while (true) { diff --git a/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java b/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java index fefd5be0fb6..91d6320c159 100644 --- a/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java +++ b/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java @@ -23,10 +23,6 @@ import com.google.cloud.pubsub.spi.v1.TopicAdminClient; import com.google.pubsub.v1.SubscriptionName; import com.google.pubsub.v1.TopicName; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -38,9 +34,15 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; -/** Tests for quickstart sample. */ +/** + * Tests for quickstart sample. + */ @RunWith(JUnit4.class) @SuppressWarnings("checkstyle:abbreviationaswordinname") public class QuickStartIT { @@ -69,7 +71,8 @@ public void run() { } } - @Rule public Timeout globalTimeout = Timeout.seconds(300); // 5 minute timeout + @Rule + public Timeout globalTimeout = Timeout.seconds(300); // 5 minute timeout @Before public void setUp() { @@ -124,7 +127,6 @@ public void testQuickstart() throws Exception { } expectedMessageIds.removeAll(receivedMessageIds); } - subscriberThread.interrupt(); assertThat(expectedMessageIds).isEmpty(); } From 5129e14df2cb912bda7cc20907f4ed50f0bf5e30 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Mon, 19 Jun 2017 14:55:25 -0700 Subject: [PATCH 16/20] remove unused code --- .../src/main/java/com/example/pubsub/SubscriberExample.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java index d11e88622c5..07e6e018e27 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java @@ -37,8 +37,6 @@ public class SubscriberExample { private static final BlockingQueue messages = new LinkedBlockingDeque<>(); - private final List receivedMessageIds = new ArrayList<>(); - static class MessageReceiverExample implements MessageReceiver { @Override @@ -48,10 +46,6 @@ public void receiveMessage(PubsubMessage message, AckReplyConsumer consumer) { } } - List getReceivedMessages() { - return ImmutableList.copyOf(receivedMessageIds); - } - public static void main(String... args) throws Exception { // set subscriber id, eg. my-subscriber-id String subscriptionId = args[0]; From 48eccccebcec3401ea3bd0020856d2b3668d4e24 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Tue, 20 Jun 2017 13:05:34 -0700 Subject: [PATCH 17/20] removing unused imports --- .../src/main/java/com/example/pubsub/SubscriberExample.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java index 07e6e018e27..69eb32181c9 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java @@ -21,12 +21,9 @@ import com.google.cloud.pubsub.spi.v1.AckReplyConsumer; import com.google.cloud.pubsub.spi.v1.MessageReceiver; import com.google.cloud.pubsub.spi.v1.Subscriber; -import com.google.common.collect.ImmutableList; import com.google.pubsub.v1.PubsubMessage; import com.google.pubsub.v1.SubscriptionName; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; From 952f0013aa60c1a040ea35e9d56c608e69f823c0 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Wed, 12 Jul 2017 14:17:23 -0700 Subject: [PATCH 18/20] formatting updates --- pubsub/cloud-client/README.md | 12 ++++++------ .../pubsub/CreatePullSubscriptionExample.java | 4 ++-- .../java/com/example/pubsub/CreateTopicExample.java | 2 +- .../java/com/example/pubsub/PublisherExample.java | 2 +- .../java/com/example/pubsub/SubscriberExample.java | 2 +- .../test/java/com/example/pubsub/QuickStartIT.java | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pubsub/cloud-client/README.md b/pubsub/cloud-client/README.md index 7ac33119138..04a89a852f4 100644 --- a/pubsub/cloud-client/README.md +++ b/pubsub/cloud-client/README.md @@ -28,28 +28,28 @@ For more samples, see the samples in #### Create a new topic ``` - mvn exec:java -Dexec.mainClass=com.example.pubsub.CreateTopicExample -Dexec.args=my-topic-id + mvn exec:java -Dexec.mainClass=com.example.pubsub.CreateTopicExample -Dexec.args=my-topic ``` #### Create a subscription ``` - mvn exec:java -Dexec.mainClass=com.example.pubsub.CreatePullSubscriptionExample -Dexec.args="my-topic-id my-subscription-id" + mvn exec:java -Dexec.mainClass=com.example.pubsub.CreatePullSubscriptionExample -Dexec.args="my-topic-id my-sub" ``` #### Publish messages ``` - mvn exec:java -Dexec.mainClass=com.example.pubsub.PublisherExample -Dexec.args=my-topic-id + mvn exec:java -Dexec.mainClass=com.example.pubsub.PublisherExample -Dexec.args=my-topic ``` -Publishes 5 messages to the topic `my-topic-id`. +Publishes 5 messages to the topic `my-topic`. #### Receive messages ``` - mvn exec:java -Dexec.mainClass=com.example.pubsub.SubscriberExample -Dexec.args=my-subscription-id + mvn exec:java -Dexec.mainClass=com.example.pubsub.SubscriberExample -Dexec.args=my-sub ``` Subscriber will continue to listen on the topic for 5 minutes and print out message id and data as messages are received. #### Testing Run the test with Maven. ``` - mvn clean -Dtest=com.example.pubsub.QuickStartIT verify + mvn -Dtest=com.example.pubsub.QuickStartIT verify ``` diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/CreatePullSubscriptionExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/CreatePullSubscriptionExample.java index 2b3838c79a0..95ecc9ab9fe 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/CreatePullSubscriptionExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/CreatePullSubscriptionExample.java @@ -31,10 +31,10 @@ public static void main(String... args) throws Exception { // Your Google Cloud Platform project ID String projectId = ServiceOptions.getDefaultProjectId(); - // Your topic ID, eg. "my-topic-id" + // Your topic ID, eg. "my-topic" String topicId = args[0]; - // Your subscription ID eg. "my-subscription-id" + // Your subscription ID eg. "my-sub" String subscriptionId = args[1]; TopicName topicName = TopicName.create(projectId, topicId); diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/CreateTopicExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/CreateTopicExample.java index d2dcb25b730..3d97ee86af5 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/CreateTopicExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/CreateTopicExample.java @@ -29,7 +29,7 @@ public static void main(String... args) throws Exception { // Your Google Cloud Platform project ID String projectId = ServiceOptions.getDefaultProjectId(); - // Your topic ID, eg. "my-topic-id" + // Your topic ID, eg. "my-topic" String topicId = args[0]; // Create a new topic diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java index 14cd327cd3f..830f61d65f6 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java @@ -44,7 +44,7 @@ private static ApiFuture publishMessage(Publisher publisher, String mess } public static void main(String... args) throws Exception { - // topic id, eg. "my-topic-id" + // topic id, eg. "my-topic" String topicId = args[0]; TopicName topicName = TopicName.create(PROJECT_ID, topicId); Publisher publisher = null; diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java index 69eb32181c9..4c51d89cf51 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java @@ -44,7 +44,7 @@ public void receiveMessage(PubsubMessage message, AckReplyConsumer consumer) { } public static void main(String... args) throws Exception { - // set subscriber id, eg. my-subscriber-id + // set subscriber id, eg. my-sub String subscriptionId = args[0]; SubscriptionName subscriptionName = SubscriptionName.create(PROJECT_ID, subscriptionId); Subscriber subscriber = null; diff --git a/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java b/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java index 91d6320c159..296af76948c 100644 --- a/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java +++ b/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java @@ -50,8 +50,8 @@ public class QuickStartIT { private ByteArrayOutputStream bout; private String projectId = ServiceOptions.getDefaultProjectId(); - private String topicId = formatForTest("my-topic-id"); - private String subscriptionId = formatForTest("my-subscription-id"); + private String topicId = formatForTest("my-topic"); + private String subscriptionId = formatForTest("my-sub"); class SubscriberRunnable implements Runnable { From 3e59626ac56e7ae00281a18187a6c8d192d20939 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Mon, 31 Jul 2017 14:22:27 -0700 Subject: [PATCH 19/20] pub/sub getting started --- pubsub/cloud-client/README.md | 2 +- .../pubsub/CreatePullSubscriptionExample.java | 6 +++++ .../example/pubsub/CreateTopicExample.java | 6 +++++ .../com/example/pubsub/PublisherExample.java | 3 ++- .../com/example/pubsub/SubscriberExample.java | 7 +++--- .../java/com/example/pubsub/QuickStartIT.java | 22 ++++++++----------- 6 files changed, 28 insertions(+), 18 deletions(-) diff --git a/pubsub/cloud-client/README.md b/pubsub/cloud-client/README.md index 04a89a852f4..7bda525e2e2 100644 --- a/pubsub/cloud-client/README.md +++ b/pubsub/cloud-client/README.md @@ -51,5 +51,5 @@ Subscriber will continue to listen on the topic for 5 minutes and print out mess #### Testing Run the test with Maven. ``` - mvn -Dtest=com.example.pubsub.QuickStartIT verify + mvn verify ``` diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/CreatePullSubscriptionExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/CreatePullSubscriptionExample.java index 0c15da8d24e..e114c42328f 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/CreatePullSubscriptionExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/CreatePullSubscriptionExample.java @@ -26,6 +26,12 @@ public class CreatePullSubscriptionExample { + /** + * Create a pull subscription. + * + * @param args topic subscriptionId + * @throws Exception exception thrown if operation is unsuccessful + */ public static void main(String... args) throws Exception { // Your Google Cloud Platform project ID diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/CreateTopicExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/CreateTopicExample.java index 22aacf92713..8aae406c8b7 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/CreateTopicExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/CreateTopicExample.java @@ -24,6 +24,12 @@ public class CreateTopicExample { + /** + * Create a topic. + * + * @param args topicId + * @throws Exception exception thrown if operation is unsuccessful + */ public static void main(String... args) throws Exception { // Your Google Cloud Platform project ID diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java index 021b358a825..debfdb29d25 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/PublisherExample.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.example.pubsub; // [START pubsub_quickstart_publisher] @@ -23,7 +24,6 @@ import com.google.protobuf.ByteString; import com.google.pubsub.v1.PubsubMessage; import com.google.pubsub.v1.TopicName; - import java.util.ArrayList; import java.util.List; @@ -43,6 +43,7 @@ private static ApiFuture publishMessage(Publisher publisher, String mess return publisher.publish(pubsubMessage); } + /** Publish messages to a topic. */ public static void main(String... args) throws Exception { // topic id, eg. "my-topic" String topicId = args[0]; diff --git a/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java index 0183ce4fdb2..6610c8a828e 100644 --- a/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java +++ b/pubsub/cloud-client/src/main/java/com/example/pubsub/SubscriberExample.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.example.pubsub; // [START pubsub_quickstart_subscriber] @@ -23,7 +24,6 @@ import com.google.cloud.pubsub.v1.Subscriber; import com.google.pubsub.v1.PubsubMessage; import com.google.pubsub.v1.SubscriptionName; - import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; @@ -43,6 +43,7 @@ public void receiveMessage(PubsubMessage message, AckReplyConsumer consumer) { } } + /** Receive messages over a subscription. */ public static void main(String... args) throws Exception { // set subscriber id, eg. my-sub String subscriptionId = args[0]; @@ -50,8 +51,8 @@ public static void main(String... args) throws Exception { Subscriber subscriber = null; try { // create a subscriber bound to the asynchronous message receiver - subscriber = Subscriber.defaultBuilder(subscriptionName, new MessageReceiverExample()) - .build(); + subscriber = + Subscriber.defaultBuilder(subscriptionName, new MessageReceiverExample()).build(); subscriber.startAsync().awaitRunning(); // Continue to listen to messages while (true) { diff --git a/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java b/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java index 22d0dcaf1bb..d396240f8c0 100644 --- a/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java +++ b/pubsub/cloud-client/src/test/java/com/example/pubsub/QuickStartIT.java @@ -23,14 +23,6 @@ import com.google.cloud.pubsub.v1.TopicAdminClient; import com.google.pubsub.v1.SubscriptionName; import com.google.pubsub.v1.TopicName; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.Timeout; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; @@ -39,10 +31,15 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; -/** - * Tests for quickstart sample. - */ +/** Tests for quickstart sample. */ @RunWith(JUnit4.class) @SuppressWarnings("checkstyle:abbreviationaswordinname") public class QuickStartIT { @@ -71,8 +68,7 @@ public void run() { } } - @Rule - public Timeout globalTimeout = Timeout.seconds(300); // 5 minute timeout + @Rule public Timeout globalTimeout = Timeout.seconds(300); // 5 minute timeout @Before public void setUp() { From e6ec7545bf49a9a32ba0f892889151419522a2ed Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Mon, 31 Jul 2017 14:26:20 -0700 Subject: [PATCH 20/20] update README instructions --- pubsub/cloud-client/README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pubsub/cloud-client/README.md b/pubsub/cloud-client/README.md index 7bda525e2e2..dc29f51055f 100644 --- a/pubsub/cloud-client/README.md +++ b/pubsub/cloud-client/README.md @@ -14,13 +14,11 @@ For more samples, see the samples in ## Quickstart #### Setup -- Install [Maven](http://maven.apache.org/) -- Install the [Google Cloud SDK](https://cloud.google.com/sdk/) and authenticate : -``` - gcloud config set project [YOUR PROJECT ID] - gcloud auth application-default login -``` -- [Enable](https://console.cloud.google.com/apis/api/pubsub.googleapis.com/overview) Pub/Sub API +- Install [Maven](http://maven.apache.org/). +- [Enable](https://console.cloud.google.com/apis/api/pubsub.googleapis.com/overview) Pub/Sub API. +- Set up [authentication](https://cloud.google.com/docs/authentication/getting-started). + +#### Build - Build your project with: ``` mvn clean package -DskipTests