Skip to content

Commit 4d65354

Browse files
authored
Web Socket - Jetty (GoogleCloudPlatform#1282)
* copy over sample * update sample URL construction * Update parent dependency
1 parent ad8ae9e commit 4d65354

File tree

9 files changed

+592
-0
lines changed

9 files changed

+592
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# App Engine Flexible Environment - Web Socket Example
2+
This sample demonstrates how to use [Websockets](https://tools.ietf.org/html/rfc6455) on [Google App Engine Flexible Environment](https://cloud.google.com/appengine/docs/flexible/java/) using Java.
3+
The sample uses the [native Jetty WebSocket Server API](http://www.eclipse.org/jetty/documentation/9.4.x/jetty-websocket-server-api.html) to create a server-side socket
4+
and the [native Jetty WebSocket Client API](http://www.eclipse.org/jetty/documentation/9.4.x/jetty-websocket-client-api.html).
5+
6+
## Sample application workflow
7+
8+
1. The sample application creates a server socket using the endpoint `/echo`.
9+
1. The homepage (`/`) provides a form to submit a text message to the server socket. This creates a client-side socket
10+
and sends the message to the server.
11+
1. The server on receiving the message, echoes the message back to the client.
12+
1. The message received by the client is stored in an in-memory cache and is viewable on the homepage.
13+
14+
The sample also provides a Javascript [client](src/main/webapp/js_client.jsp)(`/js_client.jsp`) that you can use to test against the Websocket server.
15+
16+
## Setup
17+
18+
- [Install](https://cloud.google.com/sdk/) and initialize GCloud SDK. This will
19+
```
20+
gcloud init
21+
```
22+
- If this is your first time creating an app engine application
23+
```
24+
gcloud appengine create
25+
```
26+
27+
## Local testing
28+
29+
Run using the [Jetty Maven plugin](http://www.eclipse.org/jetty/documentation/9.4.x/jetty-maven-plugin.html).
30+
```
31+
mvn jetty:run
32+
```
33+
You can then direct your browser to `http://localhost:8080/`
34+
35+
To test the Javascript client, access `http://localhost:8080/js_client.jsp`
36+
37+
## App Engine Flex Deployment
38+
39+
#### `app.yaml` Configuration
40+
41+
App Engine Flex deployment configuration is provided in [app.yaml](src/main/appengine/app.yaml).
42+
43+
Set the environment variable `JETTY_MODULES_ENABLE:websocket` to enable the Jetty websocket module on the Jetty server.
44+
45+
Manual scaling is set to a single instance as we are using an in-memory cache of messages for this sample application.
46+
47+
For more details on configuring your `app.yaml`, please refer to [this resource](https://cloud.google.com/appengine/docs/flexible/nodejs/configuring-your-app-with-app-yaml).
48+
49+
#### Deploy
50+
51+
The sample application is packaged as a war, and hence will be automatically run using the [Java 8/Jetty 9 with Servlet 3.1 Runtime](https://cloud.google.com/appengine/docs/flexible/java/dev-jetty9).
52+
53+
```
54+
mvn appengine:deploy
55+
```
56+
You can then direct your browser to `https://YOUR_PROJECT_ID.appspot.com/`
57+
58+
To test the Javascript client, access `https://YOUR_PROJECT_ID.appspot.com/js_client.jsp`
59+
60+
Note: This application constructs a Web Socket URL using `getWebSocketAddress`
61+
in the [SendServlet Class](src/main/java/com/example/flexible/websocket/jettynative/SendServlet.java)
62+
. The application assumes the latest version of the service.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<!--
2+
Copyright 2018 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
-->
16+
<project xmlns="http://maven.apache.org/POM/4.0.0"
17+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
18+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
19+
<modelVersion>4.0.0</modelVersion>
20+
21+
<groupId>org.eclipse.jetty.demo</groupId>
22+
<artifactId>native-jetty-websocket-example</artifactId>
23+
<version>1.0-SNAPSHOT</version>
24+
<packaging>war</packaging>
25+
26+
<!--
27+
The parent pom defines common style checks and testing strategies for our samples.
28+
Removing or replacing it should not effect the execution of the samples in anyway.
29+
-->
30+
<parent>
31+
<groupId>com.google.cloud.samples</groupId>
32+
<artifactId>shared-configuration</artifactId>
33+
<version>1.0.10</version>
34+
</parent>
35+
36+
<properties>
37+
<maven.compiler.target>1.8</maven.compiler.target>
38+
<maven.compiler.source>1.8</maven.compiler.source>
39+
<failOnMissingWebXml>false</failOnMissingWebXml> <!-- REQUIRED -->
40+
<appengine.maven.plugin>1.3.1</appengine.maven.plugin>
41+
<jetty.version>9.4.4.v20170414</jetty.version>
42+
</properties>
43+
44+
<dependencies>
45+
<dependency>
46+
<groupId>javax.servlet</groupId>
47+
<artifactId>javax.servlet-api</artifactId>
48+
<version>3.1.0</version>
49+
<type>jar</type>
50+
<scope>provided</scope>
51+
</dependency>
52+
<!-- To run websockets client -->
53+
<dependency>
54+
<groupId>org.eclipse.jetty.websocket</groupId>
55+
<artifactId>websocket-client</artifactId>
56+
<version>${jetty.version}</version>
57+
</dependency>
58+
<dependency>
59+
<groupId>org.eclipse.jetty.websocket</groupId>
60+
<artifactId>websocket-servlet</artifactId>
61+
<version>${jetty.version}</version>
62+
<scope>provided</scope>
63+
</dependency>
64+
<dependency>
65+
<groupId>com.google.guava</groupId>
66+
<artifactId>guava</artifactId>
67+
<version>23.0</version>
68+
</dependency>
69+
</dependencies>
70+
71+
<build>
72+
<!-- for hot reload of the web application -->
73+
<outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes
74+
</outputDirectory>
75+
<plugins>
76+
<!-- for deployment of web application -->
77+
<plugin>
78+
<groupId>com.google.cloud.tools</groupId>
79+
<artifactId>appengine-maven-plugin</artifactId>
80+
<version>${appengine.maven.plugin}</version>
81+
<configuration>
82+
</configuration>
83+
</plugin>
84+
<!-- for local testing of web application -->
85+
<plugin>
86+
<groupId>org.eclipse.jetty</groupId>
87+
<artifactId>jetty-maven-plugin</artifactId>
88+
<version>${jetty.version}</version>
89+
</plugin>
90+
</plugins>
91+
</build>
92+
</project>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright 2018 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
runtime: java
16+
env: flex
17+
manual_scaling:
18+
instances: 1
19+
20+
handlers:
21+
- url: /.*
22+
script: this field is required, but ignored
23+
24+
env_variables:
25+
JETTY_MODULES_ENABLE: websocket
26+
27+
28+
# For applications which can take advantage of session affinity
29+
# (where the load balancer will attempt to route multiple connections from
30+
# the same user to the same App Engine instance), uncomment the folowing:
31+
32+
# network:
33+
# session_affinity: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2018 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.flexible.websocket.jettynative;
18+
19+
import java.util.Collection;
20+
import java.util.Collections;
21+
import java.util.concurrent.ConcurrentLinkedDeque;
22+
import java.util.logging.Logger;
23+
import org.eclipse.jetty.websocket.api.Session;
24+
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
25+
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
26+
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
27+
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
28+
29+
/**
30+
* Basic Echo Client Socket.
31+
*/
32+
@WebSocket(maxTextMessageSize = 64 * 1024)
33+
public class ClientSocket {
34+
private Logger logger = Logger.getLogger(ClientSocket.class.getName());
35+
private Session session;
36+
// stores the messages in-memory.
37+
// Note : this is currently an in-memory store for demonstration,
38+
// not recommended for production use-cases.
39+
private static Collection<String> messages = new ConcurrentLinkedDeque<>();
40+
41+
@OnWebSocketClose
42+
public void onClose(int statusCode, String reason) {
43+
logger.fine("Connection closed: " + statusCode + ":" + reason);
44+
this.session = null;
45+
}
46+
47+
@OnWebSocketConnect
48+
public void onConnect(Session session) {
49+
this.session = session;
50+
}
51+
52+
@OnWebSocketMessage
53+
public void onMessage(String msg) {
54+
logger.fine("Message Received : " + msg);
55+
messages.add(msg);
56+
}
57+
58+
// Retrieve all received messages.
59+
public static Collection<String> getReceivedMessages() {
60+
return Collections.unmodifiableCollection(messages);
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2018 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.flexible.websocket.jettynative;
18+
19+
import javax.servlet.annotation.WebServlet;
20+
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
21+
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
22+
23+
/*
24+
* Server-side WebSocket registered as /echo servlet.
25+
*/
26+
@SuppressWarnings("serial")
27+
@WebServlet(name = "Echo WebSocket Servlet", urlPatterns = { "/echo" })
28+
public class EchoServlet extends WebSocketServlet {
29+
@Override
30+
public void configure(WebSocketServletFactory factory) {
31+
factory.register(ServerSocket.class);
32+
}
33+
}

0 commit comments

Comments
 (0)