Build a chat app using Struts 2
A basic understanding of Apache Struts 2 is needed to follow this tutorial
The need for realtime chat can’t be overemphasized. This includes realtime communication with your users which increases customer satisfaction and, as a result, make your business more credible, convenient and reduces wait times etc.
Have you ever wondered how you could add a realtime chat to your Struts 2 web application? Have you considered the number of plugins or libraries that you might need to pull in to make it work? Worry no more, Pusher got your back. In this article, I’ll work you through how to build a realtime chat app in Java Struts 2 by leveraging Pusher realtime technology.
At the end of this tutorial, we’ll have an application similar to this:
Struts 2 is an excellent MVC Web application framework for developing enterprise Java web applications. It enables rapid development of Web applications and handles most of the plumbing required in large Web applications.
What is Channels?
Pusher Channels is a hosted service that makes it super-easy to add realtime data and functionality to web and mobile applications.
💡 Pusher Channels sits as a realtime layer between your servers and your clients. Pusher maintains persistent connections to the clients - over WebSocket if possible and falling back to HTTP-based connectivity - so that as soon as your servers have new data that they want to push to the clients they can do, instantly via Pusher.
Requirements
The following tools are used in this article:
- Java SDK – Download and install Java SDK from Oracle site if you don’t have it installed already.
- Eclipse IDE – Download and install eclipse from their website.
- JavaScript (JQuery).
- Java language (you should know the basics).
- Maven (Most recent Eclipse include the Maven tooling already)
Step 1: Setting Up A Pusher App
Sign up or login to your Pusher account and create a new Pusher Channels app.
Note down your app details you just created:
app_id = "*********"
key = "***********************"
secret = "*********************"
cluster = "**"
Step 2: Setting up Struts 2 Application in Eclipse
A Struts 2 application is an ordinary Java Web application with a set of additional libraries.
Open your Eclipse IDE then go to File >> New >> Others from the menu. You should get a prompt just like the image below:
Now, Select Maven >> Maven Project then click on Next.
You should have another prompt window:
Now select your project location, this is where you want your project’s files to be stored. After that click on Next to proceed. In my case, I used the default location by just clicking next.
You will have another prompt to select an Archetype:
Select org.apache.maven.achetypes maven-achetype-webapp 1.0 then click on Next.
On this window, put in the **Group Id**
and **Artifact**
**Id**
then click on Finish.
💡 groupId will identify your project uniquely across all projects, so we need to enforce a naming schema. It has to follow the package name rules and you can create as many subgroups as you want. Look at More information about package names. eg. org.apache.maven, org.apache.commons
💡 artifactId is the name of the jar without version. If you created it then you can choose whatever name you want. If it’s a third party jar you have to take the name of the jar as it’s distributed. eg. maven, commons-math
Once done, a new project will be created for you:
Since we’ll use maven to run the application, we need to add jetty-maven-plugin
to the pom.``xml
file.
Update pom.xml with the following jetty plugin:
<build>
...
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.4.7.v20170914</version>
<configuration>
<webApp>
<contextPath>/${build.finalName}</contextPath>
</webApp>
<stopKey>CTRL+C</stopKey>
<stopPort>8999</stopPort>
<scanIntervalSeconds>10</scanIntervalSeconds>
<scanTargets>
<scanTarget>src/main/webapp/WEB-INF/web.xml</scanTarget>
</scanTargets>
</configuration>
</plugin>
</plugins>
</build>
Now, from your Eclipse IDE, right click on the project name - **chatApp**
- or any name you have chosen. Then go to **Run As**
>> **Maven build**
.
Now type in jetty:run
in the goals then click **Apply**
and then click on **Run**
.
Visit http://localhost:8080/chatApp from your browser:
Note that
chatApp
is the folder name of your project. If you have used a different name, you should change the URL accordingly.
Next, We’ll add Struts 2 to the Classpath. Now that we know we have a working Java web application, let’s add the minimal required Struts 2 framework Jar files to our web application’s class path. In pom.xml
add the following to the dependency node:
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.5.14</version>
</dependency>
Struts 2 libraries Jar files will be downloaded and added to our project when you save.
Next, add the Struts 2 plugin that will enable us to work with JSON. Add the following **pom.xml**
dependency node:
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-json-plugin</artifactId>
<version>2.5</version>
</dependency>
Step 3: Add Logging
To see what’s happening under the hood, like when errors occur which will help during debugging, let’s add a logging dependency to our application.
Add the following dependencies to **pom.xml**
dependency node:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.8.2</version>
</dependency>
Next, setup a log4j2.xml
configuration in the src/main/resources
folder which contains the following:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="com.opensymphony.xwork2" level="debug"/>
<Logger name="org.apache.struts2" level="debug"/>
<Root level="warn">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>
Step 4: Adding Pusher Java Library
Pusher has a Java library that we can use to interact with it’s API. We’ll add this to our application.
Update pom.xml dependency node with the below:
<dependency>
<groupId>com.pusher</groupId>
<artifactId>pusher-http-java</artifactId>
<version>1.0.0</version>
</dependency>
This will download and add pusher java libraries to our application.
Step 5: Adding Struts 2 Servlet Filter
To enable the Struts 2 framework to work with our web application we need to add a Servlet filter class and filter mapping to web.xml
. Below is the filter and filter-mapping nodes you should add.
Add the following to **webapp**
node in src/main/webapp/WEB-INF/web.xml
file:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Step 6: Create struts.xml
You can see this as the router for our application. Struts 2 can use either an XML configuration file or annotations (or both) to specify the relationship between a URL, a Java class, and a view page (such as index.jsp
). For our basic Struts 2 application, we’ll use a minimal XML configuration.
Create a new file as struts.xml
in the src/main/resources
folder and add the following code to it:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="default" namespace="/" extends="json-default">
<default-action-ref name="index"/>
<action name="index">
<result>/index.jsp</result>
</action>
</package>
</struts>
With the above, we now have a route of http://localhost:8080/chatApp/index.action
available in our application:
<action name="index">
<result>/index.jsp</result>
</action>
Step 7: Crafting the chat interface
Open src/main/webapp/index.jsp
and add the following code to it:
<!DOCTYPE html>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<head>
<title>Welcome To Struts 2 chat!</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/custom.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://js.pusher.com/4.1/pusher.min.js"></script>
</head>
<body>
<h1 class="text-center">Welcome To Struts 2 chat!</h1>
<div class="container" style="border: 2px solid gray;">
<!--msgbox-->
<div id="msgItems" class="container-fluid">
</div>
<!-- querybox-->
<div class="row text-center" id="queryText">
<div class="hideForm">
<div class="row">
<div class="col-xs-9">
<input type="text" class="form-control" placeholder="Type your Message Here" id="message">
</div>
<div class="col-xs-3">
<button type="button" class="btn btn-primary" id="submitMessage">Send Message</button>
</div>
</div>
</div>
<div id="chatName">
<form class="form-inline">
<div class="form-group">
<input type="text" class="form-control" id="userName" placeholder="your username">
</div>
<button type="button" class="btn btn-primary" id="startChating">Start Chating!</button>
</form>
</div>
</div>
</div>
<script src="assets/custom.js"></script>
</body>
</html>
Next create new files called src/main/webapp/assets/custom.css
and src/main/webapp/assets/custom.js
. Note that the assets folder is not created by default, we need to create it.
In the src/main/webapp/assets/``custom.css
file, add the following code:
body {
padding-top: 50px;
}
#queryText {
position : relative;
bottom : 4%;
padding: 0.3%;
background : grey;
min-width : 200px;
}
#queryText input {
width : 100%;
}
#queryText form div {
margin-left: auto;
margin-right: auto;
}
#queryText {
border : 0px solid black;
padding: 10px;
}
#chat-item {
border-bottom : 1px solid grey;
}
#msgItems div img {
background : blue;
display : inline;
}
#msgItems {
height: 400px;
overflow: scroll;
}
.hideForm{
display: none;
}
.input-large {
padding: 5px 150px;
}
In the src/main/webapp/assets/``custom.js
file, add the following code:
// Indentify every user uniquely
var uniqueId = Math.random().toString(36).substring(2) + (new Date()).getTime().toString(36);
$("#startChating").click(function() {
if( $("#userName").val() ) { // if user provides username
$("#chatName").hide();
$(".hideForm").show();
}
});
Note that we have included Pusher’s JavaScript Library in index.jsp
which will help us listen to events so we can act on them.
Now, visit the webpage again at https://localhost:8080/chatApp/.
Step 8: Creating a Struts 2 Action Class
Here, we’ll create an action class that will serve as the server for sending data to Pusher.
Create a new folder called java
in the src/main
folder.
Then create a new file src/main/java/MessageAction.java
and add the following code to it:
package com.menusms.chatApp.action;
import com.opensymphony.xwork2.ActionSupport;
import com.pusher.rest.Pusher;
import java.util.LinkedHashMap;
import java.util.Map;
public class MessageAction extends ActionSupport{
private Map<String, String> data = new LinkedHashMap<String, String>();
private String message, userName, uniqueId;
public String execute() {
//Pusher pusher = new Pusher("app_id", "key", "secret");
Pusher pusher = new Pusher("******", "****************", "*************");
pusher.setCluster("**"); // update with your pusher cluster
pusher.setEncrypted(true);
data.put("message", this.getMessage());
data.put("userName", this.getUserName());
data.put("uniqueId", this.getUniqueId());
pusher.trigger("struts-chat", "message", data);
return SUCCESS;
}
}
Here, we have declared some variables - message
, userName
, uniqueId
and data
- which will be sent to Pusher. When the execute method is called, the data is sent to Pusher (make sure you change pusher details in the execute method with the details you saved earlier).
With this, we are sending the data to the struts-chat
channel and also triggering the message
event.
💡 Channels provide a great way of organizing streams of real-time data. Here, we are
subscribing to thestruts-chat
channel (NB: The channel name can be any name you like). Once we are subscribed to a channel, we bind that channel to an event.
💡 Events can be seen as a notification of something happening on your system and are ideal for linking updates to changes in the View. In this case we want to bind to an event which is triggered whenever a user sends a message.
Next, lets add a setter and getter for the variables we have declared. Update src/main/java/MessageAction.java
with the below:
...
public Map<String, String> getData() {
return data;
}
public void setData(Map<String, String> data) {
this.data = data;
}
public String getUniqueId() {
return uniqueId;
}
public void setUniqueId(String uniqueId) {
this.uniqueId = uniqueId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Update src/main/resources/struts.xml
with the below:
<action name="message" class="com.menusms.chatApp.action.MessageAction" method="execute">
<result type="json"></result>
</action>
Now, we have a route available - http://localhost:8080/chatApp/message
. When this URL is visited, the execute method in the class MessageAction.java
will be invoked.
Step 9: Sending messages
When a user submits a message from the HTML form, we’ll send this data to our Java class where it will be sent to Pusher.
Using jQuery, we’ll send this data to the message.action
route.
Update src/main/webapp/assets/custom.js
with the following code:
$("#submitMessage").click(function() {
var userName = $("#userName").val();
var message = $("#message").val();
$.post("message.action", {
message: message,
userName: userName,
uniqueId: uniqueId
})
.done(function(data) {
//empty the message input
$("#message").val("");
});
});
Step 10: Printing messages
We need to listen for incoming messages from Pusher and display them when they are received. We’ll do this easily with the Pusher JavaScript library we have included earlier.
We’ll subscribe to a channel (this is the channel that we are pushing data to in our java code above) and bind that channel to an event.
Add the below code to src/main/webapp/assets/custom.js
:
var pusher = new Pusher('***************', {// Replace with your PUSHER_APP_KEY
cluster: '**', // Replace with your PUSHER_APP_CLUSTER
encrypted: true
});
var channel = pusher.subscribe('struts-chat');
channel.bind('message', function(data) {
var textDirection = (data.uniqueId == uniqueId) ? " text-right" : "";
$("#msgItems").append(
`<div id="chat-item" class="row` +textDirection+ `">
<div class="cols-xs-4">
<p>
<p><b>` +data.userName+ `</b></p><img src="http://placehold.it/30X30" class="img-circle img-responsive">`
+data.message+ `
</p>
</div>
</div>`
);
});
With this:
var channel = pusher.subscribe('struts-chat');
channel.bind('message', function(data) { ...
We have subscribed to the struts-chat
channel and bind it to message
event.
Conclusion
Pusher really makes life easy when it comes to adding realtime features to web applications. In this tutorial, we have been able to learn the basics of how to add chat to a Java Struts 2 application.
If you have any questions or observations, feel free to drop them in the comments section below. I would be happy to respond to you.
5 March 2018
by Gideon Onwuka