Your search did not match any results.
We suggest you try the following to help find what you’re looking for:
How to create and deploy a Java web application using WebSocket technology.
by Fernando Babadopulos and Fabiane Nardon
The following technologies will be covered in this article:
We will use Maven to set up our project structure. If you don't have Maven installed, please, read the instructions and install Maven from https://maven.apache.org/download.cgi. The default installation is enough for this article.
To set up the base project structure, run this command:
mvn -B archetype:generate \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DgroupId=com.example.realtimewebsocket \
-DartifactId=real-time-websocket
This should create the following directory tree:
real-time-websocket/
./pom.xml
./src
./src/test/java/com/example/realtimewebsocket/AppTest.java
./src/main
./src/main/java/com/example/realtimewebsocket/App.java
The pom.xml
file is used by Maven to represent the current project; it describes the structure, dependencies, and all things related to the project itself.
Under the src
folder, Maven will create two other folders: test
and main
. The application code will be in the main
folder, and the code used to test the application will be in the test
folder.
More information about how Maven works can be found here: https://maven.apache.org/guides/getting-started/.
The first step to set up our web application is to edit the pom.xml
file and include all the dependencies that we need.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>0.32-1</version>
</dependency>
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>org.webjars.bower</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.4</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
</dependencies>
The <parent>
group instructs Maven to load a pom.xml
file from Spring Framework, while the <dependencies>
group has information for every library that we will need.
spring-boot-starter-web
enables our application to have web capabilities, bind an HTTP port, have routes to a specific URL, and so on.
spring-boot-starter-websocket
is responsible for WebSocket functionalities.
All four webjars
dependencies provide an easy-to-use way to have all the JavaScript files that we need available in the project.
If you run the following command, Maven will download all the dependencies described in the pom.xml
file and compile the existing code. But we won't have any web application yet.
mvn clean install
To enable web capabilities, we need to annotate the main class of our application and instruct Spring to run, as follows:
@SpringBootApplication
public class App
{
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
}
}
The @SpringBootApplication
is the same as @Configuration
, @EnableAutoConfiguration
, and @ComponentScan
with default values used together.
Inside the main
method, the line SpringApplication.run
is responsible for starting the server and running our web application.
The default Spring HTTP port is 8080, but we can override all default configurations using an application.properties
file.
At this point, we already have a working Spring application and, because we imported a parent pom.xml
from Spring Boot (spring-boot-starter-parent
), we have a convenient way to start the application using Maven:
mvn spring-boot:run
If you run this command and point your browser to http://localhost:8080/, you should now see an error page. That error is because our application has web capabilities, but it does nothing yet. Let's fill in the gaps.
By the way, you can press Ctrl+c to stop the application.
Because our application will use WebSockets to send events in real time to a browser, we have to configure and enable it. First we create a config class like this:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/channel");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket")
.setAllowedOrigins("*")
.withSockJS();
}
}
Because we extended the AbstractWebSocketMessageBrokerConfigurer
we can override the configureMessageBroker
method and create a new message broker channel under the path /channel
. We also create an endpoint for WebSocket STOMP messages under the /websocket
path, enable access to it from all origins and, as a fallback, we instruct it to use SockJS if the browser has no WebSocket capabilities.
STOMP is a text-oriented message protocol, and we will use it to transport our messages from our application to the browser.
Our TransportMessage.java
class will have the following structure:
public class TransportMessage {
private Date date;
private DataType type;
private String data;
private Long nextUpdate;
}
We will send two types of data back to the browser, described by this DataType.java
enum:
public enum DataType {
RANDOM,
BITCOIN;
}
To produce the data, we created the DataFactory.java
class with two methods:
randomDataGenerator
produces random data.bitcoinPriceFromAPI
consumes a Bitcoin price API.We annotate DataFactory
with the @Component
annotation, so Spring can locate it.
Those two methods need to be called from time to time, for example, every 1 second, generating the data and sending it to a specific channel. All browsers subscribed to a specific channel should receive the message.
To execute our methods every 1 second, we use the @Scheduled
Spring annotation:
@Component
public class DataFactory {
private static final long BITCOIN_REFRESH = 60_000L;
private long lastBitcoinUpdate = 0l;
private String lastBitcoinPrice = null;
@Autowired
private SimpMessagingTemplate template;
@Scheduled(fixedRate = 1_000)
public void randomDataGenerator() {
TransportMessage transportMessage = new TransportMessage(DataType.RANDOM);
transportMessage.setDate(new Date());
String value = String.valueOf(Math.random());
transportMessage.setData(value);
transportMessage.setNextUpdate(1l);
this.template.convertAndSend("/channel/random", transportMessage);
}
@Scheduled(fixedRate = 1_000)
public void bitcoinPriceFromAPI() {
TransportMessage transportMessage = new TransportMessage(DataType.BITCOIN);
transportMessage.setDate(new Date());
/* omitting the API request to improve readability
.
.
.
*/
this.template.convertAndSend("/channel/bitcoin", transportMessage);
}
}
The @Autowired
annotation is responsible for the dependency injection; it automatically initializes our template variable with an instance of SimpMessagingTemplate
.
Also, to create a background task executor and start all tasks, the main
class needs the @EnableScheduling
annotation. Without this annotation, no task will be executed.
@SpringBootApplication
@EnableScheduling
public class App
{
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
}
}
At this point, the application back end is ready. We can move forward and create the front-end web page.
Under src/main/
, create a resource/static
directory, which will hold all our resources and static pages:
mkdir src/main/resources/static
Under src/main/resources/static
, create the following files: index.html
, application.js
, and application.css
.
The index.html
file should look like this:
<!DOCTYPE html>
<html>
<head>
<title>Real Time WebSocket</title>
<link href="/application.css" rel="stylesheet">
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/application.js"></script>
</head>
<body>
<div id="content">
<div id="controls">
<button id="connect_random" class="connect" type="button">Connect to Random Channel</button>
<button id="connect_bitcoin" class="connect" type="button">Connect to Bitcoin Channel</button>
<button id="disconnect" type="button" disabled="disabled">Disconnect</button>
</div>
<div id="data">
<span class="label">Next update: </span>
<div class="value" id="nextUpdate">-</div>
<span class="label"> sec.</span><br>
<span class="label">Timestamp: </span>
<div class="value" id="date">-</div><br>
<span class="label">Value: </span>
<div class="value" id="value">-</div>
</div>
</div>
</body>
</html>
In this file, we load a few JavaScript files:
sockjs.min.js
enables WebSocket capabilities in JavaScript.stomp.min.js
process STOMP messages.jquery.min.js
is used to easily manipulate the DOM.The application.js
file contains all our application-specific JavaScript and should have the following content:
var stompClient = null;
function setConnected(connected) {
$("#disconnect").prop("disabled", !connected);
}
function connect(channel) {
disconnect();
$(".connect").removeClass("connected");
$("#connect_" + channel).addClass("connected");
var socket = new SockJS('/websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
stompClient.subscribe('/channel/' + channel, function (data) {
showGreeting(JSON.parse(data.body));
});
});
}
function disconnect() {
$(".connect").removeClass("connected");
if (stompClient !== null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function showGreeting(data) {
var date;
var value;
var nextUpdate = data.nextUpdate;
$("#nextUpdate").html(nextUpdate);
switch (data.type) {
case "RANDOM":
date = data.date;
value = data.data;
break;
case "BITCOIN":
var bitcoin = JSON.parse(data.data);
date = bitcoin.time.updated;
value = bitcoin.bpi.USD.rate_float;
break
}
$("#date").html(date);
$("#value").html(value);
}
$(function () {
$("#connect_random").click(function () {
connect('random');
});
$("#connect_bitcoin").click(function () {
connect('bitcoin');
});
$("#disconnect").click(function () {
disconnect();
});
});
Finally, we create a simple application.css
file to lay out our application:
.disconnected {
background-color: white;
}
.connected {
background-color: #97de97;
}
#data {
margin: 10px;
}
.label {
display: inline;
}
.value {
display: inline;
}
Now we have a fully functional WebSocket application. Let's start the server:
mvn spring-boot:run
Point your web browser to http://localhost:8080 and you should see our application. Click any Connect button to subscribe to a specific WebSocket channel and start receiving data.
Now that our software is ready, we can package and deploy it on an application container cloud.
The first step is to create an application.properties
file under src/main/resources/
and change the server.port
value. It should read the value from an environment variable, ${PORT}
, that is automatically created by the application container.
server.port=${PORT}
Also, create a distribution.xml
file in the project root folder to hold our assembly configuration.
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>dist</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${project.basedir}</directory>
<includes>
<include>manifest.json</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>\</outputDirectory>
<includes>
<include>real-time-websocket-1.0-SNAPSHOT.jar</include>
</includes>
<excludes>
<exclude>*.zip</exclude>
</excludes>
</fileSet>
</fileSets>
</assembly>
And add a build configuration to the pom.xml
file:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.10.RELEASE</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>distribution.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
We are now ready to package our application. But before doing that, create a manifest.json
file in the project root folder; it will have all the configuration Oracle Application Container Cloud needs to run our application. Pay attention to the command
value; it contains the command that runs the application.
{
"runtime": {
"majorVersion": "8"
},
"command": "java -jar real-time-websocket-1.0-SNAPSHOT.jar",
"notes": "Real Time WebSocket enabled application DEMO"
}
Run the following Maven command to build and package our application:
mvn clean install
Maven will package our application and create a real-time-websocket-1.0-SNAPSHOT.zip
file in the "target" directory. The .zip
file contains our entire application ready to be delivered.
It also creates a real-time-websocket-1.0-SNAPSHOT.jar
file that can be used to start the server. Just remember to set the PORT
environment variable first:
export PORT=8080
java -jar real-time-websocket-1.0-SNAPSHOT.jar
It is time to deploy our application:
Log in to your Oracle cloud account at https://cloud.oracle.com/sign-in and go to Services -> Application Container.
Click the Create Application button, which is shown in Figure 1.
Figure 1. Screen where you can choose to create an application
Choose Java SE as the application platform (Figure 2), and then in the screen that appears (Figure 3) fill in the Name field, click Choose File and select the real-time-websocket-1.0-SNAPSHOT.zip
file that we previously created.
Figure 2. Screen where you can select the application platform
Figure 3. Screen where you can specify the application details
Finally, click the Create button and wait a while to have your application deployed.
Once your container is ready you can point your browser to the URL shown in the application interface and test your newly deployed application.
The code for this article is available on GitHub. You can clone our repository and start testing right now:
git clone https://github.com/babadopulos/real-time-websocket.git
Fernando Babadopulos (@babadopulos), the CTO at Tail, is a software architect, entrepreneur, and enthusiast of new technologies; he was responsible for developing some of the most popular web applications in Brazil and abroad. With more than 15 years of experience with the internet, Babadopulos specializes in the creation and design of high-performance systems and is a frequent speaker and participant in developer conferences worldwide. He is also a JavaOne program committee member. He holds a master's degree in information engineering from the Federal University of ABC and a BS in computer science from the University Center of FEI. He is a Java Champion and an Oracle Champion recognized for his contributions to the Java ecosystem and he is a Duke's Choice Award winner.
Fabiane Nardon (@fabianenardon) is a computer scientist who is passionate about creating software that will positively change the world. She was chief architect of the Sao Paulo Healthcare Information System—considered the largest Java EE application in the world—and a winner of the 2005 Duke's Choice Award. She led several communities, including the Java Tools Community at java.net, where 800+ open source projects were born. She is a frequent speaker at conferences in Brazil and abroad, including JavaOne, OSCON, Jfokus, JustJava, QCon, and more. She's also the author of several technical articles and a member of the program committee of several conferences such as JavaOne, OSCON, TDC, and QCon. She was chosen as a Java Champion by Sun Microsystems in recognition of her contribution to the Java ecosystem. Currently, she works as a Chief Scientist at Tail, where she is helping to shape new disruptive data science–based services.
More Developer Resources
Education