Build a chat app using Struts 2

Introduction

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:

chat-struts-demo

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.

PRO TIP: Pusher Channels sits as a realtime layer between your servers and your clients. It maintains persistent connections to the clients over WebSocket if possible and falling back to HTTP-based connectivity. As soon as your servers have new data that they want to push to the clients they can do, instantly via Pusher Channels.

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 for a free Pusher account account and create a new Channels app.

chat-strut-create-app

Note down your app details you just created:

1app_id  = "*********"
2    key     = "***********************"
3    secret  =  "*********************"
4    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:

chat-struts-create-new-project

Now, Select Maven > Maven Project then click Next.

You should have another prompt window:

chat-struts-new-maven-project

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:

chat-struts-select-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.

chat-struts-archetype-parameter

💡 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:

chat-struts-file-structure

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:

1<build>
2        ... 
3        <plugins>
4            <plugin>
5                <groupId>org.eclipse.jetty</groupId>
6                <artifactId>jetty-maven-plugin</artifactId>
7                <version>9.4.7.v20170914</version>
8                <configuration>
9                    <webApp>
10                        <contextPath>/${build.finalName}</contextPath>
11                    </webApp>
12                    <stopKey>CTRL+C</stopKey>
13                    <stopPort>8999</stopPort>
14                    <scanIntervalSeconds>10</scanIntervalSeconds>
15                    <scanTargets>
16                        <scanTarget>src/main/webapp/WEB-INF/web.xml</scanTarget>
17                    </scanTargets>
18                </configuration>
19            </plugin>
20        </plugins>
21    </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**.

chat-struts-running-project-editing-configuration

Now type in jetty:run in the goals then click **Apply** and then click on **Run**.

Visit http://localhost:8080/chatApp from your browser:

chat-struts-hello-world

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:

1<dependency>
2        <groupId>org.apache.struts</groupId>
3        <artifactId>struts2-core</artifactId>
4        <version>2.5.14</version>
5    </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:

1<dependency>
2           <groupId>org.apache.struts</groupId>
3           <artifactId>struts2-json-plugin</artifactId>
4           <version>2.5</version>
5    </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:

1<dependency>
2        <groupId>org.apache.logging.log4j</groupId>
3        <artifactId>log4j-core</artifactId>
4        <version>2.8.2</version>
5    </dependency>
6    <dependency>
7        <groupId>org.apache.logging.log4j</groupId>
8        <artifactId>log4j-api</artifactId>
9        <version>2.8.2</version>
10    </dependency>

Next, setup a log4j2.xml configuration in the src/main/resources folder which contains the following:

1<?xml version="1.0" encoding="UTF-8"?>
2    <Configuration>
3        <Appenders>
4            <Console name="STDOUT" target="SYSTEM_OUT">
5                <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
6            </Console>
7        </Appenders>
8        <Loggers>
9            <Logger name="com.opensymphony.xwork2" level="debug"/>
10            <Logger name="org.apache.struts2" level="debug"/>
11            <Root level="warn">
12                <AppenderRef ref="STDOUT"/>
13            </Root>
14        </Loggers>
15    </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:

1<dependency>
2      <groupId>com.pusher</groupId>
3      <artifactId>pusher-http-java</artifactId>
4      <version>1.0.0</version>
5    </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:

1<filter>
2        <filter-name>struts2</filter-name>
3        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
4    </filter>
5
6    <filter-mapping>
7        <filter-name>struts2</filter-name>
8        <url-pattern>/*</url-pattern>
9    </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:

1<?xml version="1.0" encoding="UTF-8"?>
2    <!DOCTYPE struts PUBLIC
3        "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
4        "http://struts.apache.org/dtds/struts-2.5.dtd">
5
6    <struts>
7
8        <constant name="struts.devMode" value="true" />
9
10        <package name="default" namespace="/" extends="json-default">
11         <default-action-ref name="index"/> 
12
13            <action name="index">
14                <result>/index.jsp</result>
15            </action>
16        </package>
17
18    </struts>

With the above, we now have a route of http://localhost:8080/chatApp/index.action available in our application:

1<action name="index">
2        <result>/index.jsp</result>
3    </action>

Step 7: Crafting the chat interface

Open src/main/webapp/index.jsp and add the following code to it:

1<!DOCTYPE html>
2    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
3    <head>
4      <title>Welcome To Struts 2 chat!</title>
5      <meta charset="utf-8">
6      <meta name="viewport" content="width=device-width, initial-scale=1">
7      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
8      <link rel="stylesheet" href="assets/custom.css">
9      <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
10      <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
11      <script src="https://js.pusher.com/4.1/pusher.min.js"></script>
12
13    </head>
14    <body>
15       <h1 class="text-center">Welcome To Struts 2 chat!</h1>
16        <div class="container" style="border: 2px solid gray;">
17          <!--msgbox-->
18          <div id="msgItems" class="container-fluid">
19
20          </div>
21
22                <!-- querybox-->
23                <div class="row text-center" id="queryText">
24
25                   <div class="hideForm">
26                        <div class="row">
27                            <div class="col-xs-9">
28                                 <input type="text" class="form-control" placeholder="Type your Message Here" id="message">
29                            </div>
30                            <div class="col-xs-3">
31                                <button type="button" class="btn btn-primary" id="submitMessage">Send Message</button>
32                            </div>
33                        </div>
34                   </div>
35
36                   <div id="chatName">
37                         <form class="form-inline">
38                            <div class="form-group">
39                                 <input type="text" class="form-control" id="userName" placeholder="your username">
40                            </div>
41                                 <button type="button" class="btn btn-primary" id="startChating">Start Chating!</button>
42                        </form>
43                   </div>
44
45               </div>
46
47        </div>
48
49       <script src="assets/custom.js"></script>
50    </body>
51
52    </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:

1body {
2      padding-top: 50px;
3    }
4
5    #queryText {
6      position : relative;
7      bottom : 4%;
8      padding: 0.3%;
9      background : grey;
10      min-width : 200px;
11    }
12
13    #queryText  input {
14      width : 100%;
15    }
16
17    #queryText form div {
18      margin-left: auto;
19      margin-right: auto;
20    }
21
22    #queryText {
23      border : 0px solid black;
24      padding: 10px;
25    }
26
27    #chat-item {
28      border-bottom : 1px solid grey;
29    }
30
31    #msgItems div img {
32      background : blue;
33      display : inline;
34    }
35
36    #msgItems {
37       height: 400px;
38       overflow: scroll;
39    }
40    .hideForm{
41      display: none;
42    }
43    .input-large {
44     padding: 5px 150px;
45    }

In the src/main/webapp/assets/``custom.js file, add the following code:

1// Indentify every user uniquely
2    var uniqueId = Math.random().toString(36).substring(2) + (new Date()).getTime().toString(36);
3
4    $("#startChating").click(function() {
5
6        if( $("#userName").val() ) { // if user provides username
7           $("#chatName").hide();
8           $(".hideForm").show();
9        }
10    });

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/.

chat-struts-interface

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:

1package com.menusms.chatApp.action;
2
3    import com.opensymphony.xwork2.ActionSupport;
4
5    import com.pusher.rest.Pusher;
6
7    import java.util.LinkedHashMap;
8    import java.util.Map;
9
10    public class MessageAction extends ActionSupport{
11
12         private Map<String, String> data = new LinkedHashMap<String, String>();
13
14         private String message, userName, uniqueId;
15
16         public String execute() {
17
18              //Pusher pusher = new Pusher("app_id", "key", "secret");
19               Pusher pusher = new Pusher("******", "****************", "*************");
20               pusher.setCluster("**"); // update with your pusher cluster
21               pusher.setEncrypted(true);
22
23               data.put("message", this.getMessage());
24               data.put("userName", this.getUserName());
25               data.put("uniqueId", this.getUniqueId());
26
27               pusher.trigger("struts-chat", "message", data);
28
29               return SUCCESS;
30          }
31
32    }

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 the struts-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:

1...
2           public Map<String, String> getData() {
3                return data;
4            }
5
6            public void setData(Map<String, String> data) {
7               this.data = data;
8            }
9
10            public String getUniqueId() {
11               return uniqueId;
12            }
13
14            public void setUniqueId(String uniqueId) {
15               this.uniqueId = uniqueId;
16             }
17
18             public String getUserName() {
19                return userName;
20             }
21
22             public void setUserName(String userName) {
23                this.userName = userName;
24             }
25
26             public String getMessage() {
27                return message;
28             }
29
30             public void setMessage(String message) {
31                this.message = message;
32             }
33    }

Update src/main/resources/struts.xml with the below:

1<action name="message" class="com.menusms.chatApp.action.MessageAction" method="execute">
2        <result type="json"></result>
3    </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:

1$("#submitMessage").click(function() {
2
3       var userName = $("#userName").val();
4       var message  = $("#message").val();
5
6    $.post("message.action", {
7            message: message,
8            userName: userName,
9            uniqueId: uniqueId
10        })
11        .done(function(data) {
12            //empty the message input
13            $("#message").val("");
14        });
15    });

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:

1var pusher = new Pusher('***************', {// Replace with your PUSHER_APP_KEY
2          cluster: '**', // Replace with your PUSHER_APP_CLUSTER
3          encrypted: true
4        });
5
6        var channel = pusher.subscribe('struts-chat');
7        channel.bind('message', function(data) {
8
9         var textDirection = (data.uniqueId == uniqueId) ? " text-right" : "";
10
11          $("#msgItems").append(
12                `<div id="chat-item" class="row` +textDirection+ `">
13          <div class="cols-xs-4">
14                <p>
15                    <p><b>` +data.userName+ `</b></p><img src="http://placehold.it/30X30" class="img-circle img-responsive">`
16                    +data.message+ `
17                </p>
18
19          </div>
20      </div>`
21          );
22        });

With this:

1var channel = pusher.subscribe('struts-chat');
2    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.