Building a Realtime AngularJS Dashboard using Spring Rest and MongoDB — Part 1
Building a Realtime AngularJS Dashboard using Spring Rest and MongoDB — Part 1
In this post will show you how to build a RESTful web service using Spring MVC 4.0.6, Spring Data for MongoDB 1.6.1 so that we can integrate our Realtime Dashboard Web Application with a highly efficient datastore (MongoDB 3.0). This tutorial will be making extensive use of the SIGAR libraries to gather system statistics from the host operating system where the services reside. Additionally, this tutorial we will walk you through building the web service and NoSQL database backend and illustrate you how to implement some of the CRUD operations.
I will be using AngularJS for the front end, and will be using the Polling Technique or Periodic Refresh Pattern to get statistics from the Web Service via REST calls. This will be achieved using Angular’s $interval service. More on that later on…
To further enhance the user experience, we will use AngularJS, jQuery and several different Javascript Charting and Graphing Libraries (Sparkline, NVD3, ChartJS, JustGage, and CanvGauge) to create a visually appealing user interface that is highly efficient and easy to navigate and use.
As with other posts, we are continuing to build upon using an older post (Spring RESTful Web Service Example with JSON and Jackson using Spring Tool Suite) as a foundation.
We will be providing another tutorial with the focus on the GUI side and how we structured it using the various components including AngularJS, Javascript, CSS, JQuery, and JSPs. However, for this tutorial we will focus on Spring MVC, Spring Data for persistence to MongoDB and RESTful API.
Our Realtime Dashboard AngularJS Application
Getting Started using Spring Data
The primary goal of Spring Data is to make it easy to access both legacy relational databases in addition to new data technologies like NoSQL databases, map reduce frameworks and cloud based solutions. Spring Data for MongoDB is an umbrella project which aim to keep the consistent and familiar manner of the Spring based programming paradigm for new datastores.
In order to run this tutorial yourself, you will need the following:
- Java JDK 1.6 or greater
- Spring Tool Suite (STS)
- Tomcat 7 or greater or other popular container (Weblogic, Websphere, Glassfish, JBoss, VMWare vFabric, etc). For this tutorial I am using VMware vFabric tc Server Developer Edition which is essentially an enhanced Tomcat instance integrated with Spring STS
- Spring Framework 4.0.6
- Spring Data Mongo 1.6.1
- MongoDB NoSQL Database 3.0
- AngularJS
- Bootstrap
- Sigar (System Information Gatherer And Reporter)
- jQuery Sparkline
- Nvd3 Charting
- ChartJS
- Just Gage
Project Structure — Server Side
I have added the project overview to give you a full view of the structure and show you all files contained in this sample project.

WEB-INF\Lib Folder Structure
I have included the folder structure because I would recommend including all of the SIGAR files as part of your WAR file. This library includes of the native libraries for the specific operating systems. If you do not wish to include the SIGAR libraries inside your package you will need to ensure that the Java Library Path contains the correct path to the location of the SIGAR libraries using:
-Djava.library.path=../path/lib/sigar/lib.

RESTful Web Service End Points
# | URI | Method | Description |
---|---|---|---|
1 | /status | GET | Returns a simple HTML output of server status |
2 | /statistics | GET | Returns statistics used in the four upper panels of the Dashboard UI from MongoDB repository |
3 | /cpu | GET | Retrieves details on CPU, processes, memory and swap space as well as historical running data |
4 | /osdetails | GET | Retrieves details on Operating System and Java Virtual Machine (JVM) |
5 | /getallnetworkinterfaces | GET | Finds all network interfaces that are available |
6 | /getnetworkstats?interface={interface} | GET | Retrieves statistics on network interface like speed, rxBytes, txBytes, packets, collisions, errors, etc |
7 | /networkdetails?interface={interface} | GET | Retrieves details of the network interface like name, type, flags, description, MAC address, etc |
8 | /getalldisks | GET | Retrieves a list of all available disks or devices that are mapped or available |
9 | /diskdetails?drive={drive} | GET | Provides details on drive specified including available bytes, disk reads, free bytes, total bytes, and percentage used |
10 | /heartbeat | GET | Returns the current count of heartbeat from the server |
11 | /resetheartbeat | GET | Resets the count of the heartbeat back to one (1) |
DashboardRepository Data Access Object (DAO) for MongoDB (DashboardRepository.java)
In this class you will notice two annotations being used. The first one, @Repository indicates that the class DashboardRepository fulfills the role of a Data Access Object of a repository. This class will handle all of the persistence of Statistics object and database access for us.
Please Note
Although the Dashboard Repository has all the CRUD operations included in the code, the dashboard application is only using the getStatisticsByID method which will retrieve the one record containing all the data stats. I have also decided to put all the stats into one document to avoid unnecessary I/O and enhance performance. You may decide on using individual documents or records based on your own needs.
The second annotation, @Autowired indicates that MongoTemplate is autowired from the Spring configuration, in this case our dispatcher-servlet.xml file.
package com.avaldes.dao; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.stereotype.Repository; import com.avaldes.model.Statistics; @Repository public class DashboardRepository { private static final Logger logger = LoggerFactory.getLogger(DashboardRepository.class); public static final String COLLECTION_NAME = "statistics"; @Autowired private MongoTemplate mongoTemplate; public void addStatistics(Statistics stats) { if (!mongoTemplate.collectionExists(Statistics.class)) { mongoTemplate.createCollection(Statistics.class); } logger.info("Inside addStatistics()..."); mongoTemplate.insert(stats, COLLECTION_NAME); } public Statistics getStatisticsByID(String id) { Statistics stats = mongoTemplate.findOne( Query.query(Criteria.where("_id").is(id)), Statistics.class, COLLECTION_NAME); logger.info("Inside getStatisticsByID (" + id +"), stats=" + stats); return stats; } public Statistics deleteStatistics(String id) { Statistics stats = mongoTemplate.findOne( Query.query(Criteria.where("_id").is(id)), Statistics.class, COLLECTION_NAME); mongoTemplate.remove(stats, COLLECTION_NAME); return stats; } public Statistics updateStatistics(String id, Statistics stats) { Statistics myStats = mongoTemplate.findOne( Query.query(Criteria.where("_id").is(id)), Statistics.class, COLLECTION_NAME); if (myStats != null) { logger.info("Inside updateStatistics(), updating record..."); myStats.setId(id); myStats.setTodayHeading(stats.getTodayHeading()); myStats.setTodayCount(stats.getTodayCount()); myStats.setTodayAverage(stats.getTodayAverage()); myStats.setTodayAverageSubheading(stats.getTodayAverageSubheading()); myStats.setOnboardedHeading(stats.getOnboardedHeading()); myStats.setOnboardedCount(stats.getOnboardedCount()); myStats.setOnboardedSubheading(stats.getOnboardedSubheading()); myStats.setSignupsHeading(stats.getSignupsHeading()); myStats.setSignupsCount(stats.getSignupsCount()); myStats.setSignupsSubheading(stats.getSignupsSubheading()); mongoTemplate.save(myStats, "Statistics"); return stats; } else { logger.info("Inside updateStatistics(), unable to update record..."); return null; } } }
The Model Classes (Statistics.java)
The simple model is used as a basis for storing and retrieving the fields to and from MongoDB as a document in the collection. This class contains several annotations. The first one, the @Document annotation identifies objects or entities that are going to be persisted to MongoDB. The next one, @Id is used to identify the field that will be used as an ID in MongoDB. This ID is labeled _id in MongoDB. The other two annotations are used mostly for Jackson conversion to XML and JSON respectively.
package com.avaldes.model; import java.io.Serializable; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import org.codehaus.jackson.annotate.JsonProperty; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; @Document @XmlRootElement(name = "statistics") public class Statistics implements Serializable { private static final long serialVersionUID = -1982982909309202L; @Id private String id; private String todayHeading; private int todayCount; private double todayAverage; private String todayAverageSubheading; private String onboardedHeading; private int onboardedCount; private String onboardedSubheading; private String signupsHeading; private int signupsCount; private String signupsSubheading; @XmlElement(name="id", type=String.class) @JsonProperty("id") public String getId() { return id; } public void setId(String id) { this.id = id; } @XmlElement(name="today_heading", type=String.class) @JsonProperty("today_heading") public String getTodayHeading() { return todayHeading; } public void setTodayHeading(String todayHeading) { this.todayHeading = todayHeading; } @XmlElement(name="today_count") @JsonProperty("today_count") public int getTodayCount() { return todayCount; } public void setTodayCount(int todayCount) { this.todayCount = todayCount; } @XmlElement(name="today_avg") @JsonProperty("today_avg") public double getTodayAverage() { return todayAverage; } public void setTodayAverage(double todayAverage) { this.todayAverage = todayAverage; } @XmlElement(name="today_avg_subheading", type=String.class) @JsonProperty("today_avg_subheading") public String getTodayAverageSubheading() { return todayAverageSubheading; } public void setTodayAverageSubheading(String todayAverageSubheading) { this.todayAverageSubheading = todayAverageSubheading; } @XmlElement(name="onboarded_heading", type=String.class) @JsonProperty("onboarded_heading") public String getOnboardedHeading() { return onboardedHeading; } public void setOnboardedHeading(String onboardedHeading) { this.onboardedHeading = onboardedHeading; } @XmlElement(name="onboarded_count") @JsonProperty("onboarded_count") public int getOnboardedCount() { return onboardedCount; } public void setOnboardedCount(int onboardedCount) { this.onboardedCount = onboardedCount; } @XmlElement(name="onboarded_subheading", type=String.class) @JsonProperty("onboarded_subheading") public String getOnboardedSubheading() { return onboardedSubheading; } public void setOnboardedSubheading(String onboardedSubheading) { this.onboardedSubheading = onboardedSubheading; } @XmlElement(name="signups_heading", type=String.class) @JsonProperty("signups_heading") public String getSignupsHeading() { return signupsHeading; } public void setSignupsHeading(String signupsHeading) { this.signupsHeading = signupsHeading; } @XmlElement(name="signups_count") @JsonProperty("signups_count") public int getSignupsCount() { return signupsCount; } public void setSignupsCount(int signupsCount) { this.signupsCount = signupsCount; } @XmlElement(name="signups_subheading", type=String.class) @JsonProperty("signups_subheading") public String getSignupsSubheading() { return signupsSubheading; } public void setSignupsSubheading(String signupsSubheading) { this.signupsSubheading = signupsSubheading; } @Override public String toString() { return "Statistics [id=" + id + ", todayHeading=" + todayHeading + ", todayCount=" + todayCount + ", todayAverage=" + todayAverage + ", todayAverageSubheading=" + todayAverageSubheading + ", onboardingHeading=" + onboardedHeading + ", onboardedCount=" + onboardedCount + ", onboardedSubheading=" + onboardedSubheading + ", signupsHeading=" + signupsHeading + ", signupsCount=" + signupsCount + ", signupsSubheading=" + signupsSubheading + "]"; } }
The Model Class for DiskData (DiskData.java)
The remaining Model classes are used to store information being retrieved from the SIGAR libraries. The REST calls will then make use of this stored information and relay the details back to the consumer of the service in either JSON format or XML.
package com.avaldes.model; import javax.xml.bind.annotation.XmlElement; import org.codehaus.jackson.annotate.JsonProperty; public class DiskData { private String dirName; private String devName; private String typeName; private String sysTypeName; private String options; private int type; private long flags; private boolean isOnline; private double usedPercentage; @XmlElement(name="dir_name") @JsonProperty("dir_name") public String getDirName() { return dirName; } public void setDirName(String dirName) { this.dirName = dirName; } @XmlElement(name="dev_name") @JsonProperty("dev_name") public String getDevName() { return devName; } public void setDevName(String devName) { this.devName = devName; } @XmlElement(name="type_name") @JsonProperty("type_name") public String getTypeName() { return typeName; } public void setTypeName(String typeName) { this.typeName = typeName; } @XmlElement(name="sys_type_name") @JsonProperty("sys_type_name") public String getSysTypeName() { return sysTypeName; } public void setSysTypeName(String sysTypeName) { this.sysTypeName = sysTypeName; } @XmlElement(name="options") @JsonProperty("options") public String getOptions() { return options; } public void setOptions(String options) { this.options = options; } @XmlElement(name="type") @JsonProperty("type") public int getType() { return type; } public void setType(int type) { this.type = type; } @XmlElement(name="is_online") @JsonProperty("is_online") public boolean isOnline() { return isOnline; } public void setOnline(boolean isOnline) { this.isOnline = isOnline; } @XmlElement(name="flags") @JsonProperty("flags") public long getFlags() { return flags; } public void setFlags(long flags) { this.flags = flags; } @XmlElement(name="used_percentage") @JsonProperty("used_percentage") public double getUsedPercentage() { return usedPercentage; } public void setUsedPercentage(double usedPercentage) { this.usedPercentage = usedPercentage; } @Override public String toString() { return "DiskData [dirName=" + dirName + ", devName=" + devName + ", typeName=" + typeName + ", sysTypeName=" + sysTypeName + ", options=" + options + ", type=" + type + ", flags=" + flags + ", isOnline=" + isOnline + ", usedPercentage=" + usedPercentage + "]"; } }
The Model Class for Disks (Disks.java)
package com.avaldes.model; import java.util.ArrayList; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import org.codehaus.jackson.annotate.JsonProperty; @XmlRootElement public class Disks { private ArrayList<DiskData> diskData = new ArrayList<DiskData>(); @XmlElement(name="disk") @JsonProperty("disk") public ArrayList<DiskData> getDiskData() { return diskData; } public void addDrive(String dirName, String devName, String typeName, String sysTypeName, String options, int type, long flags, boolean isOnline, double usedPercent) { DiskData data = new DiskData(); data.setDirName(dirName); data.setDevName(devName); data.setTypeName(typeName); data.setSysTypeName(sysTypeName); data.setOptions(options); data.setType(type); data.setFlags(flags); data.setOnline(isOnline); data.setUsedPercentage(usedPercent); diskData.add(data); } @Override public String toString() { return "AllDisks [diskData=" + diskData + "]"; } }
Let’s View the Realtime AngularJS Dashboard GUI Screens
As you can see from the following screenshots, the UI is quite appealing in its design.
Associated Posts
- Building a Realtime AngularJS Dashboard using Spring Rest and MongoDB -- Part 1
In this tutorial we will cover implementing a Dashboard application using Periodic Refresh Pattern (Polling) via AJAX calls from AngularJS. - Building a Realtime AngularJS Dashboard using Spring Rest and MongoDB -- Part 2
The second part tutorial continues where the first left off and continues diving into the code base. - Building a Realtime AngularJS Dashboard using Spring Rest and MongoDB -- Part 3
In this last of three part tutorial finish up our deep dive and reveal the finishing touches to our real-time application. - Build Realtime AngularJS Dashboard with AngularJS and Bootstrap UI — Part 1
In this tutorial series, our focus will be on the User Interface (UI) using AngularJS, Bootstrap, and the various Javascript Charting and Graphing Libraries (Sparkline, NVD3, ChartJS, JustGage, and CanvGauge). - Build Realtime AngularJS Dashboard with AngularJS and Bootstrap UI — Part 2
The second part tutorial continues looking at the user interface (UI) components where the first left off and continues diving into the code base. - Build Realtime AngularJS Dashboard with AngularJS and Bootstrap UI — Part 3
In this last of three part tutorial finish up our deep dive into the UI and reveal the finishing touches to our real-time application.

Please Share Us on Social Media






Leave a Reply