Back in one of my previous projects I was asked to produce a little contingency application. The schedule was tight and the scope simple. The in-house coding standard is PHP, so trying to get a classic Java EE stack in place would have been a real challenge. And, to be really honest, completely oversized. So, what then? I took the chance and gave Spring a try. I used it before, but in old versions, hidden away in the tech stack of the portal software I was plagued with at this time.
My goal was to have something the WebOps can simply put on a server with Java installed and run it. No fiddling with dozens of XML configurations and memory fine tuning. Just as easy as java -jar application.jar
.
It was the perfect call for “Spring Boot”. This Spring project is all about making it easy to bring you, the developer, up to speed and take away the need of loads of configuration and boilerplate coding.
Another thing my project was crying for was a document-oriented data storage. I mean, the main purpose of the application was to offer a digital version of a real-world paper form. So why create a relational mess if we can represent the document as a document?! I used MongoDB in a couple of small projects before, so I decided to go with it.
What has this got to do with this article? Well, I will show you how quickly you can bring together all the bits and pieces needed for a web application. Spring Boot will make a lot of things fairly easy and will keep the code minimal. And at the end you will have a JAR file, which is executable and can be deployed by just dropping it onto a server. Your WebOps will love you for it.
Let’s imagine we are about to create the next big product administration web application. As it is the next big thing, it needs a big name: Productr (this is the reason I am a software engineer and not in sales or marketing…).
Productr will do amazing things and this article will show you its early stages, which are:
- providing a simple REST interface to query all available products
- loading these products from a MongoDB
- providing a production-ready monitoring facility
- displaying all products by using a JavaScript UI
All you need to start is:
- Java 8
- Maven
- Your favourite IDE (IntelliJ, Eclipse, vi, edlin, a butterfly…)
- A browser (ok, or Internet Explorer / MS Edge, but who would really want this?!)
And for the impatient, the code is also available on GitHub.
Let’s get started
Create a pom.xml with the following content:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.0.RELEASE</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>net.h0lg.tutorials.rapid</groupId> <artifactId>rapid-resting</artifactId> <version>1.0</version> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
In these few lines a lot of stuff is already happening. Most important is the defined parent project. This will bring us a lot of useful and needed dependencies like logging, the Tomcat runtime and lots more. Thanks to Spring’s modularity, everything is re-configurable via pom.xml or dependency injection. For getting everything up quickly the defaults are absolutely fine. (Convention over configuration, anybody?)
Now, create the obligatory Maven folder structure:
mkdir -p src/main/java src/main/resources src/test/java src/test/resources
And we are settled.
Start the engines
Let’s get to work. We want to offer a REST interface to get access to our huge amount of products. So let’s start with creating a REST collection available under /api/products. To do so we have to do a few things:
- Our “data model” holding all information about our incredible products needs to be created
- We need a controller offering a method which does everything necessary to answer a GET request
- Create the main entry point for our application
The data model is pretty simple and done quickly. Just create a package called demo.model and a class called Product in it. The Product class is very straightforward:
package demo.model; import java.io.Serializable; /** * Our very important and sophisticated data model */ public class Product implements Serializable { String productId; String name; String vendor; public String getProductId() { return productId; } public void setProductId(String productId) { this.productId = productId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getVendor() { return vendor; } public void setVendor(String vendor) { this.vendor = vendor; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Product product = (Product) o; if (getProductId() != null ? !getProductId().equals(product.getProductId()) : product.getProductId() != null) return false; if (getName() != null ? !getName().equals(product.getName()) : product.getName() != null) return false; return !(getVendor() != null ? !getVendor().equals(product.getVendor()) : product.getVendor() != null); } @Override public int hashCode() { int result = getProductId() != null ? getProductId().hashCode() : 0; result = 31 * result + (getName() != null ? getName().hashCode() : 0); result = 31 * result + (getVendor() != null ? getVendor().hashCode() : 0); return result; } }
Our product has the incredible amount of 3 properties: an alphanumeric product ID, a name and a vendor (just the name, to keep things simple). It is serialisable and the getters, setters and the methods equals() & hashCode() are implemented by using my IDE’s code generation.
Alright, so creating a controller with a method to offer the GET listener it is now. Go back to your favourite IDE and create the package demo.controller and a class called ProductsController with the following content:
package demo.controller; import demo.model.Product; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; /** * This controller provides the REST methods */ @RestController @RequestMapping(value = "/", method = RequestMethod.GET) public class ProductsController { @RequestMapping(value = "/", method = RequestMethod.GET) public ListgetProducts() { List products = new ArrayList (); return products; } }
This is really everything you need to provide a REST interface. Ok, at the moment, an empty list is returned, but it is that easy to define.
The last thing missing is an entry point for our application. Just create a class called Productr in the package demo and give it the following content:
package demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * This is the entry point of our application */ @SpringBootApplication public class ProductrApplication { public static void main (String... opts) { SpringApplication.run(ProductrApplication.class, opts); } }
Spring Boot saves us a lot of keystrokes. @SpringBootApplication does a few things we would need for every web application anyway. This annotation is shorthand for the following ones:
- @Configuration
- @EnableAutoConfiguration
- @ComponentScan
Now it is time to start our application for the first time. Thanks to Spring Boot’s maven plugin, which we configured in our pom.xml, starting the application is as easy as: mvn spring-boot:run
. Just run this command in your project root directory. You prefer the lazy point-n-click way provided by your IDE? Alright, just instruct your favourite IDE to run ProductrApplication.
Once it is started, use a browser, a REST client (you should check out Postman, I love this tool) or a command line tool like curl. The address you are looking for is: http://localhost:8080/api/products/. So, with curl, the command looks like this:
curl http://localhost:8080/api/products/
Data please
Ok, returning an empty list isn’t that shiny, is it? So let’s bring in data.
In many projects a classic relational database is usually overkill (and painful if you have to use it AND scale out). This may be one reason for the hype around NoSQL databases. One (in my opinion good) example is MongoDB.
Getting MongoDB up and running is pretty easy. On Linux you can use your package manager to install it. For Debian / Ubuntu, for example, simply do: sudo apt-get install mongodb
.
For Mac, the easiest way is homebrew: brew install mongodb
and follow the instructions in the “Caveats” section.
Windows users should go with the MongoDB installer (and toi toi toi).
Alright, we just got our data store sorted. It is about time to use it.
There is one particular Spring project dealing with data – called Spring Data. And by sheer coincidence a sub-project called Spring Data MongoDB is just waiting for us. Even more, Spring Boot provides a dependency package to get up to speed instantly. No wonder that the following few lines in the pom.xml‘s <dependencies>
section are enough to bring in everything we need:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
Now, create a new package called demo.domain and put in a new interface called ProductRepository. Spring provides a pretty neat way to get rid of writing code which is usually needed to interact with a data source. Most of the basic queries are generated by Spring Data – all you need is to define an interface. A couple of query methods are available without even specifying method headers. One example is the findAll()
method, which will return all entries in the collection.
But hey, let’s see it in action instead of talking about it. The bespoke ProductRepository interface should look like this:
package demo.domain; import demo.model.Product; import org.springframework.data.mongodb.repository.MongoRepository; /** * This interface lets Spring generate a whole Repository implementation for * Products. */ public interface ProductRepository extends MongoRepository{ }
Next, create a class called ProductService in the same package. Purpose of this class is to actually provide some useful methods to query products. For now, the code is as easy as this:
package demo.domain; import demo.model.Product; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * This is a little service class we will let Spring inject later. */ @Service public class ProductService { @Autowired private ProductRepository repository; public ListgetProducts() { return repository.findAll(); } }
See how we can use repository.findAll()
without even defining it in the interface? Pretty slick, isn’t it? Especially if you are in a hurry and need to get things up quickly.
Alright, so far we prepared the foundation for the data access. I think it is time to wire it together. To do so, simply head back to our class demo.controller.ProductsController
and modify it slightly. All we have to do is to inject our shiny new ProductService service and call its getProducts()
method. The class will look like this afterwards:
package demo.controller; import demo.domain.ProductService; import demo.model.Product; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; /** * This controller provides the REST methods */ @RestController @RequestMapping("/api/products/") public class ProductsController { // Let Spring DI inject the service for us @Autowired private ProductService productService; @RequestMapping(value = "/", method = RequestMethod.GET) public ListgetProducts() { // Ask the data store for a list of products return productService.getProducts(); } }
That’s it. Start MongoDB (if not already running), start our application again (remember the mvn spring-boot:run
thingy?!) and start another GET request to http://localhost:8080/api/products/:
$ curl http://localhost:8080/api/products/
[]
Wait, still an empty list? Yes, or do you remember us putting anything into the database? Let’s change this by using the following command:
mongo localhost/test --eval "db.product.insert({productId: 'a1234', name: 'Our First Product', vendor: 'ACME'})"
This adds one product called “Our First Product” to our database. Ok, so what is our service returning now? This:
$ curl http://localhost:8080/api/products/ [{"productId":"5657654426ed9d921affc3c0","name":"Our First Product","vendor":"ACME"}]
Easy, wasn’t it?!
Looking for a little more data but no time to create it yourself? Alright, it’s nearly Christmas, so take my little test selection:
curl https://gist.githubusercontent.com/daincredibleholg/f8667a26ce2f17776903/raw/ed9b4c8ec6c9c455dc063e833af2418648928ba6/quick-web-app-product-example.json | mongoimport -d test -c product --jsonArray
Basic requirements at your fingertips
In today’s hectic days and with “microservice” culture spreading, it is getting harder and harder to keep an eye on what is really running on your servers or cloud environments. So in nearly all environments I was working on over the last years monitoring was a big thing. One common pattern is to provide health check endpoints. One can find everything from simple ping endpoints to health metrics, returning a detailed overview of business relevant metrics.
All of this is most of the times a copy-n-paste adventure and involves tackling a lot of boilerplate code. Here is what we have to do – simply add the following dependency to your pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
and restart the service. Let’s have a look what happens if we query http://localhost:8080/health:
$ curl http://localhost:8080/health
{"status":"UP","diskSpace":{"status":"UP","total":499088621568,"free":83261571072,"threshold":10485760},"mongo":{"status":"UP","version":"3.0.7"}}
This should provide sufficient data for a basic health check. If you follow the startup log messages, you’ll probably spotted a number of other endpoints. Experiment a bit and check the Actuator documentation for more information.
Show it to me
Ok, we got ourselves a REST service and some data. But we want to show this data to our users. So let’s go on and provide a page with an overview of our awesome products.
Thank Santa that there is a really active web UI community working on loads of nice and easy usable frontend frameworks and libraries. One pretty popular example is Bootstrap. It is easy to use and all the needed bits and pieces are provided via open CDNs.
We want to have a short overview of our products, so a table view would be nice. Bootstrap Table will help us with that. It is built on top of Bootstrap and also available via CDNs. What a world we live in…
But wait, where to put our HTML file? Spring Boot makes it easy, again. Just create a folder called src/main/resources/static and create a new HTML file called index.html with the following content:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Productr</title> <!-- Import Bootstrap CSS from CDNs --> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.9.1/bootstrap-table.min.css"> </head> <body> <nav class="navbar navbar-inverse"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Productr</a> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Home</a></li> <li><a href="#about">About</a></li> <li><a href="#contact">Contact</a></li> </ul> </div><!--/.nav-collapse --> </div> </nav> <div class="container"> <table data-toggle="table" data-url="/api/products/"> <thead> <tr> <th data-field="productId">Product Reference</th> <th data-field="name">Name</th> <th data-field="vendor">Vendor</th> </tr> </thead> </table> </div> <!-- Import Bootstrap, Bootstrap Table and JQuery JS from CDNs --> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.9.1/bootstrap-table.min.js"></script> </body> </html>
This file isn’t pretty complex. It is just a HTML file, which includes the minimised CSS files from the CDNs. If you see a reference like //maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css
for the first time, it is not a bad mistake that the protocol (http or https) is missing. A resource referenced that way will be loaded via the same protocol the main page got loaded with. Say, if you use http://localhost:8080/, it will use http: to load the CSS files.
The <body> block contains a navigation bar (using the HTML5 <nav> tag) and a table. The interesting part of this table definition is the provided data-url attribute. It is interpreted by Bootstrap Table to load the data. Our definition points to our previously created REST endpoint.
Which part of our JSON objects is used in which column is defined via the data-field attributes on the <th> definitions. Can you spot the matching attribute names?
Last but not least we load the needed JavaScript libraries. All Bootstrap-related JavaScript functionality needs JQuery, so this is the first library to load. Followed straight by the main Bootstrap and the Bootstrap Table JavaScript files. Each of these library files is loaded in the minimised version, to keep download times at a minimum.
Where to go now
It is fair to say that we have a really simple web application now. Well, the main purpose of this article was to show you how to get up to speed with as little code as possible. You’ve seen that sometimes just a dependency in your POM file brings you a complete new feature, without the need of any additional line of code.
Take a step back, look at what we’ve built so far and think about the next steps needed. And just start to take a look around in the Spring universe.
I think one of the most crucial steps needed next, beside adding the missing tests, is to bring in security. Check out Spring Security and its subprojects Spring Security OAuth.
More interested in “classic” web pages? Check out Spring MVC and how easy it is to integrate quite sophisticated template engines (e. g. by following this guide).
Hopefully, you enjoyed this article as much as I enjoyed its creation. I wish you all a merry Christmas and if the one or the other wants to get in touch, you can find me e. g. on Twitter, G+ and LinkedIn.
Author: Holger Steinhauer
I’m a Senior Consultant at Valtech Ltd. and work on various projects in a wide range of roles. Lately, I was the tech lead for a .net and a Java project. Technology wise I am interested in NoSQL technologies and polyglot JVM development.
gpanther December 30, 2015
FYI, it’s probably better to just use https:// every time (instead of protocol relative URLs) because (a) it is more secure and (b) protocol relative URLs don’t work if you open the page locally, using a file:// URL (for example during development).
Holger Steinhauer January 5, 2016 — Post Author
Thanks for your reply. You are right, as long as the referenced assets are always available via https, this is the better / safer approach. But I’ve seen environments, where https was limited and if you deliver / generate the assets locally, https won’t work with localhost (most of the times :)).
Jesse December 20, 2016
Can you continue this tutorial and create CRUD functions on the ui? Please
Holger Steinhauer December 20, 2016 — Post Author
Hey Jesse. I will see what I can do 🙂