Learning Spring Boot: Chapter No. 2 "Quick Start With Java"
Learning Spring Boot: Chapter No. 2 "Quick Start With Java"
Learning Spring Boot: Chapter No. 2 "Quick Start With Java"
Greg L. Turnquist
Chapter No. 2
"Quick Start with Java"
He has worked with Java, Spring, Spring Security, AspectJ, and Jython technologies and
has also developed sophisticated scripts for *nix and Windows platforms. As a wiki
evangelist, he has also deployed a LAMP-based wiki website that provides fingertip
knowledge to users.
In 2006, Greg created the Spring Python project. The Spring Framework provided
many useful features, and he wanted these features to be available when he was working
with Python. He has written Python Testing Cookbook and Spring Python 1.1 for
Packt Publishing.
He has completed a Master's degree in Computer Engineering at Auburn University and
lives in the United States with his family.
The screen is a bit long and was cut off. The following table shows you all the
settings filled in for this example. However, to see the code behind this website, visit
https://github.com/spring-io/initializr. To whet your appetite, the site is, in
fact, a Spring Boot / Groovy app using the same tools covered in the previous chapter.
[ 40 ]
Chapter 2
Value
Group
learningspringboot
Artifact
issue-manager
Name
Issue Manager
Description
Package Name
learningspringboot
Type
Gradle Project
Packaging
Jar
Java Version
1.8
Language
Java
Project dependencies
Thymeleaf
Click on the Generate Project button; it downloads starter.zip. Let's take a peek
inside the ZIP file:
$ unzip -l <downloaded zip file>
Archive:
Length
starter.zip
Date
--------
----
Time
----
Name
----
06-13-14 03:37
src/
06-13-14 03:37
src/main/
06-13-14 03:37
src/main/java/
06-13-14 03:37
src/main/java/learningspringboot/
06-13-14 03:37
src/main/resources/
06-13-14 03:37
src/test/
06-13-14 03:37
src/test/java/
06-13-14 03:37
src/test/java/learningspringboot/
1421
466
06-13-14 03:37
build.gradle
06-13-14 03:37
src/main/java/learningspringboot/Application.
06-13-14 03:37
src/main/resources/application.properties
java
0
src/test/java/learningspringboot/
------12 files
[ 41 ]
Before looking at the generated code, let's look at the build file:
// tag::plugins[]
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradleplugin:1.1.6.RELEASE")
}
}
// end::plugins[]
apply
apply
apply
apply
plugin:
plugin:
plugin:
plugin:
'java'
'eclipse'
'idea'
'spring-boot'
jar {
baseName = 'issue-manager'
version = '0.0.1-SNAPSHOT'
}
// tag::version[]
sourceCompatibility = 1.8
[ 42 ]
Chapter 2
targetCompatibility = 1.8
// end::version[]
repositories {
mavenCentral()
}
// tag::dependencies[]
dependencies {
compile("org.springframework.boot:spring-boot-starterthymeleaf")
testCompile("org.springframework.boot:spring-boot-startertest")
}
// end::dependencies[]
task wrapper(type: Wrapper) {
gradleVersion = '2.1'
}
There are a quite a few parts to this file, so let's walk through them bit by bit.
You might see some comments such as <!-- tag::x-y-z[] --> in
build.gradle and other files throughout this book. These are simple
comments that are used to help pull in subsections for more detailed
explanations and are not required to run any code you write.
[ 43 ]
This shows you our project, which is configured to pull down packages from
mavenCentral. However, more importantly, our package is using spring-bootgradle-plugin, Version 1.1.6.RELEASE. A key feature that we will see in this chapter
and through the rest of this book is Spring Boot's series of predefined versions for
many third-party libraries (not just Spring projects). By using this plugin, Spring Boot
will set the version number for any dependency we declare that it happens to manage.
Continuing to check out our build file, we can see a list of dependencies:
dependencies {
compile("org.springframework.boot:spring-boot-starterthymeleaf")
testCompile("org.springframework.boot:spring-boot-startertest")
}
running tests
The first one matches the checkbox we picked on the form (but couldn't see directly
due to the cutoff): Thymeleaf. The second one is included in all projects, given the
popularity of automated testing in this day and age.
So, what are these quirky packages? They definitely look different than any packages
we might have used in the past, which we will find out about in the next section.
Before we do that, let's look at another key setting:
sourceCompatibility = 1.8
targetCompatibility = 1.8
This specifies that the project is using Java 8. While Spring Boot provides support as
far back as Java 6, we plan to take advantage of the latest features that are out there
throughout this book.
[ 44 ]
Chapter 2
To go into more detail about starters, let's pick this one: spring-boot-starterthymeleaf. If we look at its pom.xml build file online (https://github.com/
spring-projects/spring-boot/tree/v1.1.6.RELEASE/spring-bootstarters/spring-boot-starter-thymeleaf/pom.xml), we will see the following
dependencies:
Dependency
What it provides
spring-boot-starter
spring-boot-starter-web
spring-core
thymeleaf-spring4
thymeleaf-layout-dialect
Wait, I thought this book was focused on Gradle! That's true, but
Spring Boot itself is built with Maven. It is valuable to look at any
of Spring Boot's starters in order to glean what they do.
Spring Boot is designed to help us build good apps rapidly. A key piece
of making this happen is how Boot plugs in its opinion. When we include
spring-boot-starter-thymeleaf, Spring Boot has the opinion that we'll
probably want embedded Tomcat, Jackson JSON support, JSR 303 validation,
and Spring Web MVC. So, it adds them as required dependencies.
Notice how there are no version numbers in the dependencies section? This is
because each dependency we see listed is preset with a version number supplied by
spring-boot-gradle-plugin. It's another opinion from Spring Boot about the best
version of the library to use in conjunction with all the others.
The last thing Boot does is make auto-configuration decisions. The previous chapter
showed us many examples of this, and it's happening here as well. Spring Boot
configured view resolvers, an embedded servlet container, and other components
that are commonly recommended for Spring MVC apps.
[ 45 ]
As we continue along this chapter, and throughout this book, we'll get to see more
opinions that Boot inserts (and how it backs off when we make a different decision).
This amalgamation of libraries and chosen versions is known as Spring
IO (http://spring.io/platform) and offers an out-of-the-box
virtual collection of libraries that are verified to work together. Spring
IO is very easy to use, as it's served up through the industry-standard
Maven public repositories; it's not a downloadable bundle that becomes
outdated the day after you get it.
them into the app context as beans. By default, it scans for classes found
underneath the package where the annotation is declared.
behavior.
[ 46 ]
Chapter 2
4. This dependency will load Spring Social GitHub. Since it's not a general
release, we need to add this to the repositories section:
repositories {
mavenCentral()
maven { url "https://repo.spring.io/libs-snapshot" }
}
[ 47 ]
Spring Social GitHub comes with a GitHubIssue class, but this class doesn't include
the name of the repository for a given issue. The Issue class listed in the preceding
code is basically a wrapper that adds this extra bit of information. It's designed
to be created through a constructor call that minimizes the risk of initializing it
incompletely. It also includes getter calls in order to retrieve the data fields.
[ 48 ]
Chapter 2
This class is marked as @Service, which means that it will be picked up and added
to the app context by @ComponentScan. All of Spring's component annotations
inherit from @Component, which gets them picked up by component scanning.
It has a hardcoded GitHub passcode and a hardcoded organization name, and
will fetch issues from spring-boot and spring-boot-issues. This service also has a
GitHubTemplate.
The key function of this class, which is findOpenIssues, loops through the list of
repositories, and then uses GitHubTemplate to retrieve open issues. It gathers them
into a standard list, wrapped inside the Issue object we coded earlier.
[ 50 ]
Chapter 2
The last bit of Java code that is required is a web controller that serves up a table
of issues:
package learningspringboot;
import
import
import
import
org.springframework.beans.factory.annotation.Autowired;
org.springframework.stereotype.Controller;
org.springframework.ui.Model;
org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IssueController {
private IssueManager issueManager;
@Autowired
public IssueController(IssueManager issueManager) {
this.issueManager = issueManager;
}
@RequestMapping(value = "/")
public String index(Model model) {
[ 51 ]
resources/templates/index.html:
<html xmlns:th="http://www.thymeleaf.org">
<body>
<p>Open GitHub Issues</p>
<table>
<thead>
<tr>
<td>Repo</td>
<td>Issue</td>
<td>Title</td>
</tr>
</thead>
<tbody>
<tr th:each="issue : ${issues}">
<td th:text="${issue.repo}"></td>
<td>
<a th:href="${issue.githubIssue.url}"
target="_blank">
<span
th:text="${issue.githubIssue.number}"/>
</a>
</td>
[ 52 ]
Chapter 2
<td th:text="${issue.githubIssue.title}"></td>
</tr>
</tbody>
</table>
</body>
</html>
There isn't a lot here. The core piece is the dynamically generated table. The template
uses a Thymeleaf for-each loop. The <tr th:each="issue : ${issues}"> tag
generates one <tr> row for each entry in ${issues}. From there, we are able to access
property values in order to populate text values and URL links, and also show the title
of each issue.
We can now run it at this stage! There are a couple of approaches:
____
__ _ _
__ _ \ \ \ \
) ) ) )
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::
(v1.1.6.RELEASE)
[ 54 ]
Chapter 2
With the app running, we can visit http://localhost:8080 and see the results.
org.springframework.beans.factory.InitializingBean;
org.springframework.beans.factory.annotation.Value;
org.springframework.social.github.api.GitHubIssue;
org.springframework.social.github.api.impl.GitHubTemplate;
org.springframework.stereotype.Service;
@Service
public class IssueManager implements InitializingBean {
@Value("${github.token}")
String githubToken;
@Value("${org}")
String org;
@Value("${repos}")
String[] repos;
GitHubTemplate gitHubTemplate;
@Override
public void afterPropertiesSet() throws Exception {
this.gitHubTemplate = new GitHubTemplate(githubToken);
}
public List<Issue> findOpenIssues() {
List<Issue> openIssues = new ArrayList<>();
for (String repo : repos) {
for (GitHubIssue issue : gitHubTemplate
.repoOperations().getIssues(org, repo)) {
if (issue.getState().equals("open")) {
openIssues.add(new Issue(repo, issue));
}
}
}
return openIssues;
}
}
[ 56 ]
Chapter 2
This version is almost the same as the previous IssueManager class. The difference
is that we are using Spring's @Value annotation to glean properties instead of
hardcoding them. Any of these newly defined properties can be injected from
multiple sources. They are cascaded in the following order:
As a bonus, Spring Boot provides relaxed rules on name binding. This means that we
can set githubToken using either github.token or GITHUB_TOKEN as command-line
environment variables. This provides universal support on *nix, Mac, and Windows.
There is no need to write any code to process properties files!
Let's try out the latter approach:
$ GITHUB_TOKEN=ccdbf257f052a594a0e7bd2823a69ae38a48ffb1 ./gradlew clean
bootRun
[ 57 ]
By default, mobile support is switched off. This value activates Spring Mobile's
ability to switch views based on a user agent lookup.
Other settings are available, as listed in the following snippet:
spring.mobile.devicedelegatingviewresolver.normalPrefix=
spring.mobile.devicedelegatingviewresolver.normalSuffix=
[ 58 ]
Chapter 2
spring.mobile.devicedelegatingviewresolver.mobilePrefix=mobile/
spring.mobile.devicedelegatingviewresolver.mobileSuffix=
spring.mobile.devicedelegatingviewresolver.tabletPrefix=tablet/
spring.mobile.devicedelegatingviewresolver.tabletSuffix=
These are the defaults inside Spring Boot and are not in our app.
There's not much here. For the moment, it just displays an alternate message, which
indicates that we hit the right view. We'll add to it later in this chapter. Let's just see
how it switches views properly; it's time to fire it up.
Are we ready to test things out? Well, not quite yet. Spring Mobile uses the browser's
user agent to make decisions based on the type of screen that is viewing the site.
While we can use a real mobile device to test things out, this process can be quite
cumbersome and time consuming. It's best to find a plugin for our browser in order
to switch the user agent settings automatically.
[ 59 ]
With the app running and our browser switched to mobile view, let's visit
http://localhost:8080.
[ 60 ]
Chapter 2
We can see the mobile/index.html template with ease. Things are lined up in order
to create a truly mobile experience!
In the previous chapter, we dropped the files into public/. In this case, as we
have a conventional Gradle project layout, the same target folder is at src/main/
resources/. By the way, we have the same options (/META-INF/resources/, /
resources/, /static/, /public/). We simply have to place them at src/main/
resources/.
Looks like we're ready to go! Execute the following steps:
$ bower init
[?] name: issue-manager
[?] version: 0.1.0
[?] description: Learning Spring Boot - Issue Manager
[?] main file:
[?] what types of modules does this package expose? amd
[ 61 ]
cached git://github.com/jobrapido/jquery-
bower jquery-mobile-bower#*
validate 1.4.2 against git://github.com/
jobrapido/jquery-mobile-bower.git#*
bower jquery#~1.10.0
git#1.10.2
bower jquery#~1.10.0
jquery/jquery.git#~1.10.0
cached git://github.com/jquery/jquery.
validate 1.10.2 against git://github.com/
bower jquery-mobile-bower#~1.4.2
bower#1.4.2
install jquery-mobile-
bower jquery#~1.10.0
install jquery#1.10.2
jquery-mobile-bower#1.4.2 src/main/resources/public/jquery-mobile-bower
jquery#1.10.2
jquery#1.10.2 src/main/resources/public/jquery
We can see that it installed jQuery Mobile 1.4.2 along with jQuery 1.10.0. This gives
us all we need in order to craft a mobile web page.
Again, this isn't a complete introduction to jQuery Mobile.
A great resource for learning how to drive various widgets
is jQuery Mobile's showcase, which is available at
http://demos.jquerymobile.com/1.4.2.
[ 62 ]
Chapter 2
To see our complete lineup of JavaScript modules for the frontend, check out
bower.json:
{
"name": "issue-manager",
"version": "0.1.0",
"authors": [
"Greg Turnquist <[email protected]>"
],
"description": "Learning Spring Boot - Issue Manager",
"license": "ASL",
"homepage": "http://blog.greglturnquist.com/category/learningspring-boot",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"src/main/resources/public/",
"test",
"tests"
],
"dependencies": {
"jquery-mobile-bower": "~1.4.2"
}
}
It's time to put the pedal to the metal and load up jQuery Mobile's CSS and
JavaScript components. The following page is a very simple mobile layout that
we need to create at src/main/resources/templates/mobile/index.html:
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta name="viewport" content="width=device-width, initialscale=1" />
<link rel="stylesheet" href="jquery-mobilebower/css/jquery.mobile-1.4.2.css" />
<script src="jquery/jquery.js"></script>
<script src="jquery-mobile-bower/js/jquery.mobile1.4.2.js"></script>
</head>
<body>
<div data-role="page" id="home">
<div data-role="header" data-position="fixed">
[ 63 ]
Where do we begin? Okay, maybe it's a little bit bigger than you expected. However,
if you've done any major hacking on HTML, you might recognize that this isn't as
huge as other frontend systems. So, let's break it up:
<html xmlns:th="http://www.thymeleaf.org">
The first line is a giveaway that this is most certainly a Thymeleaf template.
It declares the th namespace, and we plan to take full advantage of this later
in the code:
<head>
<meta name="viewport" content="width=device-width, initialscale=1" />
<link rel="stylesheet" href="jquery-mobilebower/css/jquery.mobile-1.4.2.css" />
<script src="jquery/jquery.js"></script>
<script src="jquery-mobile-bower/js/jquery.mobile1.4.2.js"></script>
</head>
The header section starts off by creating some viewport settings. Essentially, it's
saying that the browser should use the full width of the device and that we are
completely zoomed in. Double-tapping the screen of your phone won't cause it
to zoom in any more. Next, we are loading up jQuery Mobile's CSS style sheet.
Then, we load up jQuery and jQuery Mobile JavaScript modules.
[ 64 ]
Chapter 2
jQuery Mobile is a declarative toolkit, which means that we only have to lay out a
set of key elements, and when the package finishes loading it will apply mobile CSS
styling. In this case, we have a top level <div data-role="page" id="home"> tag,
which defines a mobile "page." The term "page" is wrapped in scary quotes because
it isn't the same thing as an HTML page. jQuery Mobile can support multiple pages
and will only display the current one.
As this is a single page app, we aren't going any deeper into
multiple pages. Instead, we'll focus on the other parts.
Inside the "home" page, there is a <div data-role="header" dataposition="fixed"> tag. This fragment defines what appears at the top. Basically, a
header can show up to three components on the device: the left, the middle, and the
right. In our case, there is an anchor tag and a header. This automatically gets shifted
to the left and middle spots upon rendering. The anchor tag will be rendered as a
button but with a home icon instead of any text. The header tag takes the middle
slot. For our example, there isn't anything that can be put in the right slot.
[ 65 ]
After this, we get to the meat of the page: <div data-role="content">. This is the
content and is where most of the device's real estate will be put to work. It's very
analogous to the desktop version of things. The exception is that instead of a table
with rows, we are creating a list view of listed items. jQuery Mobile will convert
every line item into a button.
The anchor tag inside the list item has the URL. When you click on one of these
mobile buttons, it will open a new tab in our browser. The button's text shows you
the title as the text value.
This seems to do the trick! Using our desktop browser and Ultimate User Agent
Switcher has made it easy to build this mobile frontend. However, nothing is complete
without a check from a real mobile device. We'll see how to do this a bit later.
[ 66 ]
Chapter 2
BUILD SUCCESSFUL
[ 67 ]
Spring Boot initially builds a traditional JAR file. This file contains the compiled
class files, all the public resource files such as our jQuery Mobile code and HTML
templates, and the pom file. We can find it at build/libs/issue-manager-0.0.1SNAPSHOT.jar.original. This JAR file isn't runnable. In fact, it doesn't even have
third-party dependencies; this is by design. Such a JAR file can only be used to build
a bigger artifact, such as a WAR file.
In the spirit of runnable apps, Spring Boot's plugin takes another step. It creates a
new JAR file based on the original one and then adds third-party dependencies and
some support code in order to load the libraries. This can be found at build/libs/
issue-manager-0.0.1-SNAPSHOT.jar:
$ ls build/libs
issue-manager-0.0.1-SNAPSHOT.jar
issue-manager-0.0.1-SNAPSHOT.jar.original
____
__ _ _
__ _ \ \ \ \
) ) ) )
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::
(v1.1.6.RELEASE)
[ 68 ]
Chapter 2
2014-06-24 23:48:39.278 ... : JSR-330 'javax.inject.Inject' annotation
found a...
2014-06-24 23:48:39.864 ... : Server initialized with port: 8080
2014-06-24 23:48:40.156 ... : Starting service Tomcat
2014-06-24 23:48:40.157 ... : Starting Servlet Engine: Apache
Tomcat/7.0.54
2014-06-24 23:48:40.288 ... : Initializing Spring embedded
WebApplicationContext
2014-06-24 23:48:40.289 ... : Root WebApplicationContext: initialization
compl...
2014-06-24 23:48:40.935 ... : Mapping servlet: 'dispatcherServlet' to [/]
2014-06-24 23:48:40.938 ... : Mapping filter: 'hiddenHttpMethodFilter'
to: [/*]
2014-06-24 23:48:41.687 ... : Mapped URL path [/**/favicon.ico] onto
handler o...
2014-06-24 23:48:41.803 ... : Mapped "{[/],methods=[],params=[],headers=[
],con...
2014-06-24 23:48:41.805 ... : Mapped "{[/error],methods=[],params=[],head
ers=[...
2014-06-24 23:48:41.805 ... : Mapped "{[/error],methods=[],params=[],head
ers=[...
2014-06-24 23:48:41.831 ... : Mapped URL path [/**] onto handler of type
[clas...
2014-06-24 23:48:41.831 ... : Mapped URL path [/webjars/**] onto handler
of ty...
2014-06-24 23:48:42.042 ... : Registering beans for JMX exposure on
startup
2014-06-24 23:48:42.106 ... : Tomcat started on port(s): 8080/http
2014-06-24 23:48:42.107 ... : Started Application in 4.513 seconds (JVM
runnin...
Now we can visit http://localhost:8080 and either view the desktop version or
the mobile version. If we check for the local IP address of our machine, we can view
it from a mobile device that's on the same Wi-Fi network.
If you are on a Mac or *nix system, it's possible to find the local IP address
by typing ifconfig | grep inet | grep -v 127.0.0.1.
[ 69 ]
Assuming that we get our computer and phone on the same network, we can easily
view the mobile version of the app.
We can see the list of tickets open on the GitHub repositories. If we click on one,
it will take us to GitHub, where we can easily view the details of the issue.
Chapter 2
==> Downloading https://downloads.sf.net/project/machomebrew/Bottles/
cloudfoundry-cli-6.1.1.mavericks.bottle.tar.gz
Already downloaded: /Library/Caches/Homebrew/cloudfoundry-cli6.1.1.mavericks.bottle.tar.gz
==> Pouring cloudfoundry-cli-6.1.1.mavericks.bottle.tar.gz
Email> [email protected]
Password>
Authenticating...
OK
Org:
FrameworksAndRuntimes
Space:
development
We are prompted for the API endpoint. In Pivotal's commercial instance of CF, this
will be https://api.run.pivotal.io. If you are running a different instance, your
API endpoint will be different. We must then use our e-mail/password credentials.
After getting in, we might have to pick our organization and space for deployment.
In all likelihood, your options will be different than what's shown in the preceding
console output.
At this point, we can deploy our app as follows:
$ cf push issue-manager-gturnquist -p target/issue-manager-0.0.1SNAPSHOT.jar -m 512M
Creating app issue-manager-gturnquist in org FrameworksAndRuntimes /
space development as [email protected]...
OK
Uploading issue-manager-gturnquist...
Uploading app files from: target/issue-manager-0.0.1-SNAPSHOT.jar
Uploading 1.7M, 474 files
OK
First, let's examine the arguments used to push our app to CF:
The memory was increased to 512 MB. It has been observed that Spring
Boot apps need more than the minimum memory for web-based apps.
(Why? I'm not sure at the time of writing this.)
[ 72 ]
Chapter 2
However, wait a second; cf says that the deployment failed! Why is that?
Well, it appears to be giving us a clue by showing us how to check the logfiles:
$ cf logs issue-manager-gturnquist --recent
...
2014-06-15T00:35:23.45-0500 [App/0]
... Could not resolve placeholder
'github.token' in string value "${github.token}"
...
Okay, we've supplied an environmental variable that will be associated with the
cloud-hosted instance of this app. We can push updated JAR files all we want. As
long as we don't delete the remote app flat out, it's environmental settings will stick:
$ cf push issue-manager-gturnquist -p target/issue-manager-0.0.1SNAPSHOT.jar
...
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
0 of 1 instances running, 1 starting
1 of 1 instances running
App started
state
#0
1G
running
since
cpu
2014-06-15 12:38:26 AM
0.0%
memory
251.4M of 512M
disk
108.3M of
[ 74 ]
Chapter 2
Perfect! This looks identical to what we saw earlier, except that we don't have to visit
port 8080, and it's available anywhere on the Internet.
If we visit the same site from our desktop browser, we can see the original look
and feel.
These two Spring Boot starters activate Spring Boot Actuator as well the CRaSH
remote shell support.
[ 75 ]
In the previous chapter, we saw some detailed screenshots and explored how to
view these endpoints in our browser. All this is quite useful. However, one thing
that was mentioned was the possibility of writing a script in order to consume the
metrics. What would it take to start gathering metric data every second and dump
it into a CSV file that can be read with Excel? Let's start by creating an independent
script called metrics.groovy in the root folder of our project:
package learningspringboot
@Grab("groovy-all")
import groovy.json.*
@EnableScheduling
class MetricsCollector {
def url = "http://localhost:8080/metrics"
def slurper = new JsonSlurper()
def keys = slurper.parse(new URL(url)).keySet()
.findAll{
it.startsWith("counter")
}
def header = false;
@Scheduled(fixedRate = 1000L)
void run() {
if (!header) {
println(keys.join(','))
header = true
}
def metrics = slurper.parse(new URL(url))
println(keys.collect{metrics[it]}.join(','))
}
}
notably, JsonSlurper)
[ 76 ]
Chapter 2
The first time run() executes, it will print out a header with each key's name
joined by commas (the CSV format)
Finally, using the list of keys, each data point is gathered and printed out,
spliced together by commas (CSV)
To use the script, we first need to fire up our new and improved Issue Manager app
with Spring Boot Actuator turned on:
$ GITHUB_TOKEN=ccdbf257f052a594a0e7bd2823a69ae38a48ffb1 ./gradlew clean
bootRun
With the Actuator up and running, we can now launch our metrics collection script
in another shell (in the same folder in which metrics.groovy lives):
$ spring run -q metrics.groovy | tee metrics.csv
counter.status.200.autoconfig,counter.status.200.beans,counter.
status.200.conf...
1,1,1,1,1,1,1,1,21,1,2,1
1,1,1,1,1,1,1,1,22,1,2,1
1,1,1,1,1,1,1,1,23,1,2,1
1,1,1,1,1,1,1,1,24,1,2,1
1,1,1,1,1,1,1,1,25,1,2,1
1,1,1,1,1,1,1,1,26,1,2,1
1,1,1,1,1,1,1,1,27,1,2,1
1,1,1,1,1,1,1,1,28,1,2,1
1,1,1,1,1,1,1,1,29,1,2,1
1,1,1,1,1,1,1,1,30,1,2,1
1,1,1,1,1,1,1,1,31,1,2,1
1,1,1,1,1,1,1,1,32,1,2,1
1,1,1,1,1,1,1,1,33,1,2,1
1,1,1,1,1,1,1,1,34,1,2,1
1,1,1,1,1,1,1,1,35,1,2,1
1,1,1,1,1,1,1,1,36,1,2,1
[ 77 ]
Assuming we have done that, we can now open metrics.csv using Excel.
Gee, that's no fun. The only metrics that are increasing appear to be our script hitting
/metrics. Well, duh! If only there was a way to automatically visit other sites. Oh,
but there is! Groovy is a powerful platform. Why don't we write a script that can
load test our website at the same time? Let's do this as follows:
package learningspringboot
@Grab('org.codehaus.gpars:gpars:1.1.0')
import groovyx.gpars.GParsPool
import groovy.util.logging.*
@Slf4j
class LoadTester implements CommandLineRunner {
void run(String[] args) {
GParsPool.withPool(8) {
def loadset = ["http://localhost:8080"]*100
loadset.eachParallel { url ->
def results = url.toURL().text
log.info("Hit ${url}")
}
}
}
}
[ 78 ]
Chapter 2
So, what's all this? It's not that long, but it's packed with lots of features:
This effectively hits the website 100 times, as fast as eight cores can go. Looking at
the following metrics screenshot, we can see that counter.status.200.root is now
up to 100.
With this setup, let's launch spring run metrics.groovy | tee metrics.csv in
one shell while we run spring run load_test.groovy in another.
[ 79 ]
metrics.groovy and adjust it to have a rolling time limit. We could then serve up
the content using a little Spring MVC. Cash in on Spring MVC's WebSocket support,
and we could have the screens update dynamically. Program managers love eye
candy, right?
There's no telling what requirements we'll get from system administrators and
managers. Spring Boot's easy-to-consume endpoints provide us with the tools we
need in our main application as well as the support tools we'll build in order to
manage things.
[ 80 ]
Chapter 2
Summary
In this chapter, we used http://start.spring.io to create a bare bones project
with Gradle support. We plugged in Spring Social GitHub and used it to scan
multiple repositories for open issues. Then, we used Spring Mobile and jQuery
Mobile to create an alternative mobile frontend. We learned how to use Spring Boot's
ber easy property support. We bundled it up into a runnable JAR file and deployed
it to Cloud Foundry. Finally, we added production-ready support and did a little bit
of load testing while gathering metrics.
In the next chapter, we will explore all the tools at our disposal that can be used to
debug and manage Spring Boot applications.
[ 81 ]
Alternatively, you can buy the book from Amazon, BN.com, Computer Manuals and
most internet book retailers.
www.PacktPub.com