From 46830efa057df76ca6a8d86679418287dfaa6574 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Mon, 25 Sep 2017 17:08:15 -0700 Subject: [PATCH 1/3] Adding Pub/Sub app engine sample --- appengine-java8/pom.xml | 2 + appengine-java8/pubsub/README.md | 71 ++++++++++++++ appengine-java8/pubsub/pom.xml | 89 +++++++++++++++++ appengine-java8/pubsub/sample_message.json | 1 + .../com/example/appengine/pubsub/Message.java | 53 ++++++++++ .../appengine/pubsub/MessageRepository.java | 30 ++++++ .../pubsub/MessageRepositoryImpl.java | 98 +++++++++++++++++++ .../example/appengine/pubsub/PubSubHome.java | 47 +++++++++ .../appengine/pubsub/PubSubPublish.java | 65 ++++++++++++ .../example/appengine/pubsub/PubSubPush.java | 78 +++++++++++++++ .../flexible/pubsub/PubSubPublishTest.java | 50 ++++++++++ .../flexible/pubsub/PubSubPushTest.java | 63 ++++++++++++ .../src/main/webapp/WEB-INF/appengine-web.xml | 9 ++ .../pubsub/src/main/webapp/index.jsp | 24 +++++ 14 files changed, 680 insertions(+) create mode 100644 appengine-java8/pubsub/README.md create mode 100644 appengine-java8/pubsub/pom.xml create mode 100644 appengine-java8/pubsub/sample_message.json create mode 100644 appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/Message.java create mode 100644 appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/MessageRepository.java create mode 100644 appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/MessageRepositoryImpl.java create mode 100644 appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/PubSubHome.java create mode 100644 appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/PubSubPublish.java create mode 100644 appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/PubSubPush.java create mode 100644 appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPublishTest.java create mode 100644 appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPushTest.java create mode 100644 appengine-java8/pubsub/src/main/webapp/WEB-INF/appengine-web.xml create mode 100644 appengine-java8/pubsub/src/main/webapp/index.jsp diff --git a/appengine-java8/pom.xml b/appengine-java8/pom.xml index 9c203faf31c..66e1c0eaa98 100644 --- a/appengine-java8/pom.xml +++ b/appengine-java8/pom.xml @@ -70,6 +70,8 @@ postgres + pubsub + requests remote-client diff --git a/appengine-java8/pubsub/README.md b/appengine-java8/pubsub/README.md new file mode 100644 index 00000000000..89904714820 --- /dev/null +++ b/appengine-java8/pubsub/README.md @@ -0,0 +1,71 @@ +# Using Google Cloud Pub/Sub on App Engine Standard Java 8 Environment + +The home page of this application provides a form to publish messages using Google/Cloud PubSub. The application +then receives these published messages over a push subscription endpoint and then stores in Google Cloud Datastore. +The home page provides a view of the most recent messages persisted in storage. + +## Clone the sample app + +Copy the sample apps to your local machine, and cd to the pubsub directory: + +``` +git clone https://github.com/GoogleCloudPlatform/java-docs-samples +cd java-docs-samples/appengine-java8/pubsub +``` + +## Setup + +- Make sure [`gcloud`](https://cloud.google.com/sdk/docs/) is installed and initialized: +``` + gcloud init +``` +- If this is the first time you are creating an App Engine project +``` + gcloud app create +``` +- For local development, [set up](https://cloud.google.com/docs/authentication/getting-started) authentication +- [Enable](https://console.cloud.google.com/launcher/details/google/pubsub.googleapis.com) Pub/Sub API + +- Create a topic +``` +gcloud beta pubsub topics create +``` + +- Create a push subscription, to send messages to a Google Cloud Project URL such as https://.appspot.com/push. +The verification token is used to ensure that the end point only handles requests that are sent matching the verification token. +``` +gcloud beta pubsub subscriptions create \ + --topic \ + --push-endpoint \ + https://.appspot.com/pubsub/push?token= \ + --ack-deadline 30 +``` + +## Run +Set the following environment variables and run using shown Maven command. You can then +direct your browser to `http://localhost:8080/` + +``` +export PUBSUB_TOPIC= +export PUBSUB_VERIFICATION_TOKEN= +mvn appengine:run +``` + +## Send fake subscription push messages with: + +``` + curl -H "Content-Type: application/json" -i --data @sample_message.json + "localhost:8080/pubsub/push?token=" +``` + +## Deploy + +Update the environment variables `PUBSUB_TOPIC` and `PUBSUB_VERIFICATION_TOKEN` in +[`appengine-web.xml`](src/main/webapp/WEB-INF/appengine-web.xml), +then: + +``` + mvn appengine:deploy +``` + +Direct your browser to `https://project-id.appspot.com`. diff --git a/appengine-java8/pubsub/pom.xml b/appengine-java8/pubsub/pom.xml new file mode 100644 index 00000000000..592cdfac045 --- /dev/null +++ b/appengine-java8/pubsub/pom.xml @@ -0,0 +1,89 @@ + + + + 4.0.0 + war + 1.0-SNAPSHOT + com.example.flexible + appengine-pubsub + + + appengine-java8-samples + com.google.cloud + 1.0.0 + .. + + + + 1.8 + 1.8 + false + 1.3.1 + 9.4.4.v20170414 + + + + + javax.servlet + javax.servlet-api + 3.1.0 + jar + provided + + + + + com.google.cloud + google-cloud-pubsub + 0.24.0-beta + + + com.google.cloud + google-cloud-datastore + 1.6.0 + + + + + + junit + junit + test + + + org.mockito + mockito-all + 1.10.19 + test + + + + + ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + + com.google.cloud.tools + appengine-maven-plugin + ${appengine.maven.plugin} + + + + + + + + diff --git a/appengine-java8/pubsub/sample_message.json b/appengine-java8/pubsub/sample_message.json new file mode 100644 index 00000000000..1c0e04caa1a --- /dev/null +++ b/appengine-java8/pubsub/sample_message.json @@ -0,0 +1 @@ +{"message":{"data":"dGVzdA==","attributes":{},"messageId":"91010751788941","publishTime":"2017-04-05T23:16:42.302Z"}} diff --git a/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/Message.java b/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/Message.java new file mode 100644 index 00000000000..555d328cb38 --- /dev/null +++ b/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/Message.java @@ -0,0 +1,53 @@ +/** + * 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.pubsub; + +/** + * A message captures information from the Pubsub message received over the push endpoint and is + * persisted in storage. + */ +public class Message { + private String messageId; + private String publishTime; + private String data; + + public Message(String messageId) { + this.messageId = messageId; + } + + public String getMessageId() { + return messageId; + } + + public void setMessageId(String messageId) { + this.messageId = messageId; + } + + public String getPublishTime() { + return publishTime; + } + + public void setPublishTime(String publishTime) { + this.publishTime = publishTime; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } +} diff --git a/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/MessageRepository.java b/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/MessageRepository.java new file mode 100644 index 00000000000..d68e210f560 --- /dev/null +++ b/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/MessageRepository.java @@ -0,0 +1,30 @@ +/** + * 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.pubsub; + +import java.util.List; + +public interface MessageRepository { + + /** Save message to persistent storage. */ + void save(Message message); + + /** + * Retrieve most recent stored messages. + * @param limit number of messages + * @return list of messages + */ + List retrieve(int limit); +} diff --git a/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/MessageRepositoryImpl.java b/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/MessageRepositoryImpl.java new file mode 100644 index 00000000000..8d8e5a6b18e --- /dev/null +++ b/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/MessageRepositoryImpl.java @@ -0,0 +1,98 @@ +/** + * 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.pubsub; + +import com.google.cloud.datastore.Datastore; +import com.google.cloud.datastore.DatastoreOptions; +import com.google.cloud.datastore.Entity; +import com.google.cloud.datastore.Key; +import com.google.cloud.datastore.KeyFactory; +import com.google.cloud.datastore.Query; +import com.google.cloud.datastore.QueryResults; +import com.google.cloud.datastore.StructuredQuery; +import java.util.ArrayList; +import java.util.List; + +/** Storage for Message objects using Cloud Datastore. */ +public class MessageRepositoryImpl implements MessageRepository { + + private static MessageRepositoryImpl instance; + + private String messagesKind = "messages"; + private KeyFactory keyFactory = getDatastoreInstance().newKeyFactory().setKind(messagesKind); + + @Override + public void save(Message message) { + // Save message to "messages" + Datastore datastore = getDatastoreInstance(); + Key key = datastore.allocateId(keyFactory.newKey()); + + Entity.Builder messageEntityBuilder = Entity.newBuilder(key) + .set("messageId", message.getMessageId()); + + if (message.getData() != null) { + messageEntityBuilder = messageEntityBuilder.set("data", message.getData()); + } + + if (message.getPublishTime() != null) { + messageEntityBuilder = messageEntityBuilder.set("publishTime", message.getPublishTime()); + } + datastore.put(messageEntityBuilder.build()); + } + + @Override + public List retrieve(int limit) { + // Get Message saved in Datastore + Datastore datastore = getDatastoreInstance(); + Query query = + Query.newEntityQueryBuilder() + .setKind(messagesKind) + .setLimit(limit) + .addOrderBy(StructuredQuery.OrderBy.desc("publishTime")) + .build(); + QueryResults results = datastore.run(query); + + List messages = new ArrayList<>(); + while (results.hasNext()) { + Entity entity = results.next(); + Message message = new Message(entity.getString("messageId")); + String data = entity.getString("data"); + if (data != null) { + message.setData(data); + } + String publishTime = entity.getString("publishTime"); + if (publishTime != null) { + message.setPublishTime(publishTime); + } + messages.add(message); + } + return messages; + } + + private Datastore getDatastoreInstance() { + return DatastoreOptions.getDefaultInstance().getService(); + } + + private MessageRepositoryImpl() { + } + + // retrieve a singleton instance + public static synchronized MessageRepositoryImpl getInstance() { + if (instance == null) { + instance = new MessageRepositoryImpl(); + } + return instance; + } +} diff --git a/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/PubSubHome.java b/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/PubSubHome.java new file mode 100644 index 00000000000..b9b551de376 --- /dev/null +++ b/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/PubSubHome.java @@ -0,0 +1,47 @@ +/** + * 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.pubsub; + +import java.util.List; + +public class PubSubHome { + + private static MessageRepository messageRepository = MessageRepositoryImpl.getInstance(); + private static int MAX_MESSAGES = 10; + + /** + * Retrieve received messages in html. + * + * @return html representation of messages (one per row) + */ + public static String getReceivedMessages() { + List messageList = messageRepository.retrieve(MAX_MESSAGES); + return convertToHtmlTable(messageList); + } + + private static String convertToHtmlTable(List messages) { + StringBuilder sb = new StringBuilder(); + for (Message message : messages) { + sb.append(""); + sb.append("" + message.getMessageId() + ""); + sb.append("" + message.getData() + ""); + sb.append("" + message.getPublishTime() + ""); + sb.append(""); + } + return sb.toString(); + } + + private PubSubHome() { } +} diff --git a/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/PubSubPublish.java b/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/PubSubPublish.java new file mode 100644 index 00000000000..72f15161702 --- /dev/null +++ b/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/PubSubPublish.java @@ -0,0 +1,65 @@ +/** + * 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.pubsub; + +import com.google.cloud.ServiceOptions; +import com.google.cloud.pubsub.v1.Publisher; +import com.google.protobuf.ByteString; +import com.google.pubsub.v1.PubsubMessage; +import com.google.pubsub.v1.TopicName; +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.http.HttpStatus; + +@WebServlet(name = "Publish with PubSub", value = "/pubsub/publish") +public class PubSubPublish extends HttpServlet { + + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) + throws IOException, ServletException { + Publisher publisher = this.publisher; + try { + String topicId = System.getenv("PUBSUB_TOPIC"); + // create a publisher on the topic + if (publisher == null) { + publisher = Publisher.defaultBuilder( + TopicName.create(ServiceOptions.getDefaultProjectId(), topicId)) + .build(); + } + // construct a pubsub message from the payload + final String payload = req.getParameter("payload"); + PubsubMessage pubsubMessage = + PubsubMessage.newBuilder().setData(ByteString.copyFromUtf8(payload)).build(); + + publisher.publish(pubsubMessage); + // redirect to home page + resp.sendRedirect("/"); + } catch (Exception e) { + resp.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + } + + private Publisher publisher; + + public PubSubPublish() { } + + PubSubPublish(Publisher publisher) { + this.publisher = publisher; + } +} diff --git a/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/PubSubPush.java b/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/PubSubPush.java new file mode 100644 index 00000000000..225757a8744 --- /dev/null +++ b/appengine-java8/pubsub/src/main/java/com/example/appengine/pubsub/PubSubPush.java @@ -0,0 +1,78 @@ +/** + * 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.pubsub; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import java.io.IOException; +import java.util.Base64; +import java.util.stream.Collectors; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@WebServlet(value = "/pubsub/push") +public class PubSubPush extends HttpServlet { + + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) + throws IOException, ServletException { + String pubsubVerificationToken = System.getenv("PUBSUB_VERIFICATION_TOKEN"); + // Do not process message if request token does not match pubsubVerificationToken + if (req.getParameter("token").compareTo(pubsubVerificationToken) != 0) { + resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + // parse message object from "message" field in the request body json + // decode message data from base64 + Message message = getMessage(req); + try { + messageRepository.save(message); + // 200, 201, 204, 102 status codes are interpreted as success by the Pub/Sub system + resp.setStatus(102); + } catch (Exception e) { + resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + private Message getMessage(HttpServletRequest request) throws IOException { + String requestBody = request.getReader().lines().collect(Collectors.joining("\n")); + JsonElement jsonRoot = jsonParser.parse(requestBody); + String messageStr = jsonRoot.getAsJsonObject().get("message").toString(); + Message message = gson.fromJson(messageStr, Message.class); + // decode from base64 + String decoded = decode(message.getData()); + message.setData(decoded); + return message; + } + + private String decode(String data) { + return new String(Base64.getDecoder().decode(data)); + } + + private final Gson gson = new Gson(); + private final JsonParser jsonParser = new JsonParser(); + private MessageRepository messageRepository; + + PubSubPush(MessageRepository messageRepository) { + this.messageRepository = messageRepository; + } + + public PubSubPush() { + this.messageRepository = MessageRepositoryImpl.getInstance(); + } +} diff --git a/appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPublishTest.java b/appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPublishTest.java new file mode 100644 index 00000000000..c01c0b9a44f --- /dev/null +++ b/appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPublishTest.java @@ -0,0 +1,50 @@ +/** + * 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.flexible.pubsub; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.api.gax.core.SettableApiFuture; +import com.google.cloud.pubsub.v1.Publisher; +import com.google.protobuf.ByteString; +import com.google.pubsub.v1.PubsubMessage; +import org.junit.Test; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class PubSubPublishTest { + + @Test + public void servletPublishesPayloadMessage() throws Exception { + assertNotNull(System.getenv("PUBSUB_TOPIC")); + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getParameter("payload")).thenReturn("test-message"); + + HttpServletResponse response = mock(HttpServletResponse.class); + Publisher publisher = mock(Publisher.class); + PubsubMessage message = PubsubMessage.newBuilder() + .setData(ByteString.copyFromUtf8("test-message")).build(); + when(publisher.publish(eq(message))).thenReturn(SettableApiFuture.create()); + PubSubPublish pubSubPublish = new PubSubPublish(publisher); + // verify content of published test message + pubSubPublish.doPost(request, response); + verify(publisher, times(1)).publish(eq(message)); + } +} diff --git a/appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPushTest.java b/appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPushTest.java new file mode 100644 index 00000000000..ebb84c3156c --- /dev/null +++ b/appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPushTest.java @@ -0,0 +1,63 @@ +package com.example.flexible.pubsub; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; + +/** + * 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. + */ +import java.io.BufferedReader; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class PubSubPushTest { + + @Test + public void messageReceivedOverPushEndPointIsSaved() throws Exception { + MessageRepository messageRepository = mock(MessageRepository.class); + List messages = new ArrayList<>(); + doAnswer((invocation) -> { + messages.add((Message)invocation.getArguments()[0]); + return null; + } + ).when(messageRepository).save(any(Message.class)); + HttpServletRequest request = mock(HttpServletRequest.class); + assertNotNull(System.getenv("PUBSUB_VERIFICATION_TOKEN")); + when(request.getParameter("token")) + .thenReturn(System.getenv("PUBSUB_VERIFICATION_TOKEN")); + + HttpServletResponse response = mock(HttpServletResponse.class); + BufferedReader reader = mock(BufferedReader.class); + when (request.getReader()).thenReturn(reader); + Stream requestBody = Stream.of( + "{\"message\":{\"data\":\"dGVzdA==\",\"attributes\":{}," + + "\"messageId\":\"91010751788941\",\"publishTime\":\"2017-04-05T23:16:42.302Z\"}}"); + when(reader.lines()).thenReturn(requestBody); + PubSubPush servlet = new PubSubPush(messageRepository); + assertEquals(messages.size(), 0); + servlet.doPost(request, response); + assertEquals(messages.size(), 1); + } +} + diff --git a/appengine-java8/pubsub/src/main/webapp/WEB-INF/appengine-web.xml b/appengine-java8/pubsub/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 00000000000..13cefc05511 --- /dev/null +++ b/appengine-java8/pubsub/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,9 @@ + + true + java8 + + + + + + \ No newline at end of file diff --git a/appengine-java8/pubsub/src/main/webapp/index.jsp b/appengine-java8/pubsub/src/main/webapp/index.jsp new file mode 100644 index 00000000000..fa12f02a14d --- /dev/null +++ b/appengine-java8/pubsub/src/main/webapp/index.jsp @@ -0,0 +1,24 @@ +<%@ page import="com.example.flexible.pubsub.PubSubHome" %> + + + + + An example of using PubSub on App Engine Flex + +

Publish a message

+
+ + + +
+

Last received messages

+ + + + + + + <%= PubSubHome.getReceivedMessages() %> +
IdDataPublishTime
+ + From a4f8beee38f16ee377625740756b81699ef13a65 Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Mon, 25 Sep 2017 17:11:08 -0700 Subject: [PATCH 2/3] updating README --- appengine-java8/pubsub/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/appengine-java8/pubsub/README.md b/appengine-java8/pubsub/README.md index 89904714820..8a338d71ab6 100644 --- a/appengine-java8/pubsub/README.md +++ b/appengine-java8/pubsub/README.md @@ -1,5 +1,11 @@ # Using Google Cloud Pub/Sub on App Engine Standard Java 8 Environment +This sample demonstrates how to use [Google Cloud Pub/Sub][pubsub] +from [Google App Engine standard environment][ae-docs]. + +[pubsub]: https://cloud.google.com/pubsub/docs/ +[ae-docs]: https://cloud.google.com/appengine/docs/java/ + The home page of this application provides a form to publish messages using Google/Cloud PubSub. The application then receives these published messages over a push subscription endpoint and then stores in Google Cloud Datastore. The home page provides a view of the most recent messages persisted in storage. From c0276f29176a6a640629aa901c87842eb69f179e Mon Sep 17 00:00:00 2001 From: Jisha Abubaker Date: Tue, 26 Sep 2017 09:40:25 -0700 Subject: [PATCH 3/3] removing unit tests updating README based on review --- appengine-java8/pubsub/README.md | 5 +- appengine-java8/pubsub/pom.xml | 13 ---- appengine-java8/pubsub/sample_message.json | 2 +- .../flexible/pubsub/PubSubPublishTest.java | 50 --------------- .../flexible/pubsub/PubSubPushTest.java | 63 ------------------- 5 files changed, 5 insertions(+), 128 deletions(-) delete mode 100644 appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPublishTest.java delete mode 100644 appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPushTest.java diff --git a/appengine-java8/pubsub/README.md b/appengine-java8/pubsub/README.md index 8a338d71ab6..2852d718eab 100644 --- a/appengine-java8/pubsub/README.md +++ b/appengine-java8/pubsub/README.md @@ -38,7 +38,10 @@ gcloud beta pubsub topics create ``` - Create a push subscription, to send messages to a Google Cloud Project URL such as https://.appspot.com/push. + The verification token is used to ensure that the end point only handles requests that are sent matching the verification token. +You can use `uuidgen` on MacOS X, Windows, and Linux to generate a unique verification token. + ``` gcloud beta pubsub subscriptions create \ --topic \ @@ -47,7 +50,7 @@ gcloud beta pubsub subscriptions create \ --ack-deadline 30 ``` -## Run +## Run locally Set the following environment variables and run using shown Maven command. You can then direct your browser to `http://localhost:8080/` diff --git a/appengine-java8/pubsub/pom.xml b/appengine-java8/pubsub/pom.xml index 592cdfac045..fe47c70e8f2 100644 --- a/appengine-java8/pubsub/pom.xml +++ b/appengine-java8/pubsub/pom.xml @@ -57,19 +57,6 @@ 1.6.0 - - - - junit - junit - test - - - org.mockito - mockito-all - 1.10.19 - test - diff --git a/appengine-java8/pubsub/sample_message.json b/appengine-java8/pubsub/sample_message.json index 1c0e04caa1a..bb912195ba1 100644 --- a/appengine-java8/pubsub/sample_message.json +++ b/appengine-java8/pubsub/sample_message.json @@ -1 +1 @@ -{"message":{"data":"dGVzdA==","attributes":{},"messageId":"91010751788941","publishTime":"2017-04-05T23:16:42.302Z"}} +{"message":{"data":"dGVzdA==","attributes":{},"messageId":"91010751788941","publishTime":"2017-09-25T23:16:42.302Z"}} diff --git a/appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPublishTest.java b/appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPublishTest.java deleted file mode 100644 index c01c0b9a44f..00000000000 --- a/appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPublishTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * 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.flexible.pubsub; - -import static org.junit.Assert.assertNotNull; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.google.api.gax.core.SettableApiFuture; -import com.google.cloud.pubsub.v1.Publisher; -import com.google.protobuf.ByteString; -import com.google.pubsub.v1.PubsubMessage; -import org.junit.Test; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -public class PubSubPublishTest { - - @Test - public void servletPublishesPayloadMessage() throws Exception { - assertNotNull(System.getenv("PUBSUB_TOPIC")); - HttpServletRequest request = mock(HttpServletRequest.class); - when(request.getParameter("payload")).thenReturn("test-message"); - - HttpServletResponse response = mock(HttpServletResponse.class); - Publisher publisher = mock(Publisher.class); - PubsubMessage message = PubsubMessage.newBuilder() - .setData(ByteString.copyFromUtf8("test-message")).build(); - when(publisher.publish(eq(message))).thenReturn(SettableApiFuture.create()); - PubSubPublish pubSubPublish = new PubSubPublish(publisher); - // verify content of published test message - pubSubPublish.doPost(request, response); - verify(publisher, times(1)).publish(eq(message)); - } -} diff --git a/appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPushTest.java b/appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPushTest.java deleted file mode 100644 index ebb84c3156c..00000000000 --- a/appengine-java8/pubsub/src/main/test/com/example/flexible/pubsub/PubSubPushTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.example.flexible.pubsub; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import org.junit.Before; -import org.junit.Test; - -/** - * 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. - */ -import java.io.BufferedReader; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Stream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -public class PubSubPushTest { - - @Test - public void messageReceivedOverPushEndPointIsSaved() throws Exception { - MessageRepository messageRepository = mock(MessageRepository.class); - List messages = new ArrayList<>(); - doAnswer((invocation) -> { - messages.add((Message)invocation.getArguments()[0]); - return null; - } - ).when(messageRepository).save(any(Message.class)); - HttpServletRequest request = mock(HttpServletRequest.class); - assertNotNull(System.getenv("PUBSUB_VERIFICATION_TOKEN")); - when(request.getParameter("token")) - .thenReturn(System.getenv("PUBSUB_VERIFICATION_TOKEN")); - - HttpServletResponse response = mock(HttpServletResponse.class); - BufferedReader reader = mock(BufferedReader.class); - when (request.getReader()).thenReturn(reader); - Stream requestBody = Stream.of( - "{\"message\":{\"data\":\"dGVzdA==\",\"attributes\":{}," - + "\"messageId\":\"91010751788941\",\"publishTime\":\"2017-04-05T23:16:42.302Z\"}}"); - when(reader.lines()).thenReturn(requestBody); - PubSubPush servlet = new PubSubPush(messageRepository); - assertEquals(messages.size(), 0); - servlet.doPost(request, response); - assertEquals(messages.size(), 1); - } -} -