Tuesday, July 12, 2016

Spring Boot Toot

Overview

This tutorial is intended to be the first in a series that details how to use Spring Boot to build a web application. In the first chapter we will walk through setting up a development environment with Eclipse, Gradle, and Git source control, and getting a basic ‘Hello World’ application up and running locally as well as hosted on Heroku. Future tutorials, should they happen, will delve deeper into Spring Boot by creating a user management framework that supports user registration, login and edit. This is currently a work in progress and any comments/criticizms are welcome.

1.1 Introduction

For just about any web project, you need a login and user framework. By the end of this tutorial, you are left with a usable login and user framework. Being a Java guy, two thoughts came to mind. One, why wasn’t there a framework assembled for the Java stack that was as complete and elegant as Rails? All of the projects I have worked on were cobbled together technologies. Two, why wasn’t there a guide for a Java stack as useful and complete as the Rails Tutorial? In an attempt to answer the first question, I embarked on an effort to create a Java framework by going through the Rails Tutorial and implementing each section as closely as I could with a Java stack. I came close, but some things are just done differently in Java. What I was left with after some considerable effort was an initial stab at creating a framework, so I abandoned the work for another initiative. In my exploration I discovered a few Java stacks that attempted to be more complete bundles for web apps. Grails is one, but this requires the developer to forgo Java for Groovy. Dropwizard was another and I implemented a small project with it. I liked it. Dropwizard has a large community, however, it seems to be off by itself and is built on Jersey. The functionality of Dropwizard is quite comprehensive, but it too seems cobbled together. Spring Boot is another rapid deployment framework that leverages everything Spring.

After abandoning the first initiative to create my own framework, I set out to resolve the second question of why there was no resource comparable to the Rails Tutorial for a Java stack. I needed to develop a web front end for a project, and decided to implement the product of Rails Tutorial using Spring Boot, and to cover the progress in my own tutorial… and here we are!

1.2 Up and running

1.2.1 Development environment

I have used the Eclipse IDE professionally now for several years. It is an amazingly functional free resource. Eclipse is well suited for use with Spring Boot projects built with Gradle as Gradle can configure the project for compatibility with Eclipse, and Eclipse is able to build and run and debug a Spring Boot application from within the IDE. Debugging your Spring Boot application from within Eclipse is a tremendous convenience.

Go to the eclipse site (https://eclipse.org/) and download the installer for you your system. My OS is 64-bit Windows. Open the installer and choose the ‘Eclipse IDE for Java EE Developers’ distribution for installation. Eclipse has many distributions for various programming languages and purposes. Next, select an installation location. The default location is [your user subdirectory]\eclipse\jee-mars. For the purposes of this tutorial the default location will suffice. Click the orange ‘Install’ button.

You can manage your installation directories any way you like, but I like to have a version specific directory so I can install and use multiple versions of Eclipse. This is because I may want to use the specific version of Eclipse a was developed with so I can put off upgrading the project to work with a newer version of Eclipse. This rarely becomes a problem, but this is a defensive measure to resolve any compatibility conflicts that may occur. Plus, you may have an arbitrary number of plugins added to an Eclipse installation and you may not need or want some of these in your new installation but a project may depend on one or more of them.

Once the installer has installed Eclipse, you can open it. Click on the large green ‘LAUNCH’ button. In the future you can launch eclipse from the desktop icon which should be named ‘Eclipse Jee Mars’ or from the application menu. The first time you open Eclipse, you will be prompted for a workspace location. This is where you your projects will be stored. I usually create a workspace folder under my named subfolder called ‘workspace.’ This is also the default. Go ahead and create [user dir]/workspace. Select the ‘Use this as the default and do not ask again.’ checkbox to remember this decision. You can always switch workspaces from inside Eclipse if you need to.

The first time you open Eclipse, you will be presented with a ‘Welcome’ tab. This is not an Eclipse tutorial, so any further explorations of this capable IDE are left to the reader. Go ahead and close the welcome tab. Now you should see what is called the ‘Java EE’ perspective and a series of grouped views. We won’t be creating a project from inside Eclipse for this tutorial as we will be automating our build with Gradle. In the next sections we will be detailing how to create a project with Gradle and import it into Eclipse.

1.2.2 Build with Gradle

I’ve chosen Gradle as the build automation system. I have chosen this over Maven (or other build tool) as it seems to be more flexible than Maven, and it dispenses with the verbose xml syntax. Plus, it seems to be having some uptake in the industry, and Spring Boot guides all have a section on building with Gradle. I am going to halt discussing Gradle now, as I want to introduce another concept before describing a basic Gradle build script.

1.2.3 Gradle Wrapper

Gradle Wrapper is an ingenious little mechanism that wraps the Gradle command line. With Gradle Wrapper, the developer merely declares what version of gradle to use to build the project. When a user invokes a build with Gradle Wrapper, the declared version of Gradle is downloaded and installed in a separate space and used to build the project. The reason this is useful is twofold. One, if you have multiple projects that were built with differing versions of Gradle, you don’t need to seek out and maintain separate versions, Gradle Wrapper does this for you. A build file with the required version of Gradle is checked into source control and anyone who checks out that source has all they need to build the project. Secondly, since you do not need to download and install Gradle yourself, you are freed up to use the latest version of Gradle on any projects you may be working on, and Gradle can evolve more freely without the need to satisfy any desires for backward compatibility. Interestingly, you install the wrapper using Gradle, so we’ll talk about getting and installing Gradle, and working with Gradle Wrapper in the next section.

1.2.4 Back to Gradle

Now that we have learned about Gradle Wrapper, lets install Gradle and the Wrapper, and put together a gradle build file to see the everything in action.

Go to the Gradle web site (http://gradle.org/) and download Gradle. At the time of this writing, the latest version is 2.11. Download the complete distribution. Unzip the downloaded archive to a location on your computer. I usually extract it to the version specific directory in my named subfolder, so in this case, [user dir]/gradle-2.11. Next, add the ‘bin’ folder to your path. On Windows 10 you can edit your path by:

  1. right-click on the start button, selecting ‘System’ and then ‘Advanced system settings’ in the left-hand navigation menu.
  2. On the ‘System Properties’ dialog, under the ‘Advanced’ tab, click ‘Environment Variables…’
  3. Find the ‘path’ variable under User or System variables. I just add it to my User path variable. Select the path variable and click ‘Edit…’
  4. Click ‘New’ and enter ‘C:\Users[user dir]\gradle-2.11.’
  5. Click ‘Ok’ and exit out of all of the dialog boxes.

Now when you type ‘gradle’ at the command prompt you should see a welcome message. For a command prompt, I use an application called ConEmu (https://conemu.github.io/), as this adds a lot of linux-like functionality over the legacy windows command prompt. It does seem the new Windows command prompt has added a lot of this functionality, but ConEmu is still nicer on the eyes out of the box. I have also installed Vim so I can easily modify text files through my command prompt, but you may edit files with any application you like.

Now lets add Gradle Wrapper to a project:

  1. Open your command prompt and change directory to C:\Users[user dir]\workspace.
  2. Create a new directory to hold the project we will be using for this tutorial. Call it ‘spring_boot_tutorial.’ Cd to the new directory.
  3. Run the command ‘gradle wrapper’

This creates some files and subdirectories in your project directory. From this point on we will invoke build operations through Gradle Wrapper.

Next, we need to create some of the project directories, and the ‘gradle.build’ file that will contain instructions on dependencies and how to build our application.

In the project base directory, ‘C:\Users[user dir]\workspace\spring_boot_tutorial’, create a file called ‘build.gradle’. This is the gradle build file. Through ConEmu, you can just type ‘touch build.gradle,’ but it should be straight forward to create this file using any method you like. In build.gradle, add the following:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.3.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'

jar {
    baseName = 'spring-boot-tut'
    version =  '0.1.0'
}

repositories {
    mavenCentral()
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web") {
        exclude module: "spring-boot-starter-tomcat"
    }
    compile("org.springframework.boot:spring-boot-starter-jetty")
    compile("org.springframework.boot:spring-boot-starter-actuator")
    testCompile("junit:junit")
}

task wrapper(type: Wrapper) {
    gradleVersion = '2.11'
}

Below are some explanations of the various sections in the gradle build file.

The ‘buildscript’ block defines the repository and dependencies required by the build script itself. This may seem confusing and redundant given mavenCentral is given as a repository some lines below this block. The difference is that the build script itself is a Groovy program that potentially has its own dependencies exclusive of the application being built. Here, the spring-boot-gradle-plugin includes various purpose built tasks required by Gradle to build and run a Spring Boot application. This plugin can be found in the Maven Central repository.

The ‘apply’ notation is a method call that adds several plugins to the build.

  • The ‘java’ plugin adds several new tasks, domain objects, and conventions to the project. For example, thanks to this plugin, we know that all source should be located under src/main/java.
  • The ‘eclipse’ plugin enables us to prep the project with files and configuration needed for Eclipse to see our project as an Eclipse project. This configures the source directories and classpaths needed to build the project within Eclipse. Once the project is configured for Eclipse, you will be able to build, run and even debug the project from within Eclipse.
  • The ‘idea’ plugin is similar to the ‘eclipse’ plugin, only for the Idea IDE. It is needed here as it adds some tasks that are useful even if you are not using the Idea IDE.
  • The ‘spring-boot’ plugin adds several features to help packaging and running Spring Boot applications.

The ‘jar’ block merely sets a couple of properties that tell Gradle what the name and version of the jar file produced by the build should be.

The ‘repositories’ block denotes the Maven Central repository which is the location of several project dependencies.

The ‘dependencies’ block declares what jars to download and include in the build. When you run a Gradle build the dependencies listed here will be downloaded and added to the classpath of the build. If you run the ‘eclipse’ task, as we’ll see later, these dependencies will be added to the Eclipse project classpath so no configuration is necessary from within Eclipse.

Finally, the ‘task wrapper’ block sets a property that tells Gradle wrapper what version of Gradle to download and use to build the project. This block actually adds code to the wrapper task run from the command line.

Now go to the command line and type ‘gradlew build.’ You will see several jars downloaded from the maven repository. The build should also fail as you have not yet added a main class.

Next run ‘gradlew eclipse.’ This downloads the dependencies and assembles the eclipse classpath. You should now be able to import the project into Eclipse. Any time you make changes to the directory structure or dependencies, you should rerun the ‘eclipse’ task to update the Eclipse configuration.

To import the new project into Eclipse;

  1. Open Eclipse and select ‘File->Import…’
  2. Select ‘General->Existing Projects into Workspace,’ and click ‘Next’
  3. Click ‘Browse’ and navigate to the ‘spring_boot_tutorial’ directory we created earlier. Click ‘Ok.’ You should see a ‘spring_boot_turtorial’ selection in the ‘Projects’ list. This is recognizable as an Eclipse project due to the ‘eclipse’ task we ran previously.
  4. Click ‘Finish’

Now you should see a project in the left hand pane titled ‘Project Explorer’ named ‘spring_boot_tutorial.’ Expanding the project should display the dependencies, a build directory, a gradle directory, some gradlew scripts, and the gradle.build file. If you right click on the project and select ‘Build Path->Configure Build Path…’ you will see one of Eclipse’s primary configuration dialogs. If you click on the ‘Libraries’ tab, you will see each of the libraries downloaded and assembled by the ‘eclipse’ task. Without the ‘eclipse’ task, each of these dependencies would have to be added to the Eclipse classpath manually. If you click on the ‘Source’ tab, you can see there are no source folders configured on the build path yet. We’ll work on that next.

Gradle requires source files to be in specific directories so it can find them come build time. Many of these are configurable, but the default locations suffice for a new project. The important source file locations are:

  • src/main/java - All Java source belongs in this directory.
  • src/main/resources - Resources, such as view templates and properties files belong in this directory.
  • src/test/java - Test code goes here. This is helpful when testing package private classes and methods as the test package structure can mirror the main package structure. Also, test cases in this directory are only compiled and executed when tests are run during a build. They are not included in the final production build.
  • src/test/resources - Resources required specifically for test cases.

Let’s add these directories to the project. Create each directory in the list above directly under the ‘workspace\spring_boot_tutorial’ directory and rerun ‘gradlew eclipse’.

Now go back to Eclipse, right-click on the project and select ‘Refresh.’ You should see the directories you just created appear under the expanded project in the Project Explorer. You can also see the icons to the left of each folder appear as an open folder with a small ‘package’ inside. Eclipse includes a lot of detail in these icons that can help you determine what you are looking at with just a glance. Here we can see that these directories are packages which implies they are available on the build path. If you right-click the project and and select ‘Build Path->Configure Build Path…’ and select the ‘Source’ tab, you should see four new entries in the ‘Source folders on build path’ list. Again, the gradle eclipse plugin has done this for us.

1.3 Let’s make an app!

1.3.1 A brief overview

The most brief overview of Spring Boot is MVC. In fact, Spring Boot is Spring MVC at its core. MVC stands for ‘Model-View-Controller.’ Spring’s implementation of MVC is fairly straightforward and simplified through the use of annotations. We’ll get into the details of MVC and Springs MVC implementation in later chapters. For now we just want to get the most basic application up and running.

1.3.2 Hello world!

We will now create a basic ‘Hello World’ application that displays the text ‘Hello World’ in the browser. We will build and run this application from within Eclipse and from the command line with Gradle in order to introduce you to the different ways to build and deploy applications with Spring Boot and Gradle.

To run any Spring Boot application, you need an ‘Application’ class. This class contains a ‘main’ method that will start your application and wire together all the beans required to bring up a basic web application. Many defaults are provided so that very little configuration is necessary to get started. To start, create the following ‘Application’ class under ‘src/main/java/org/mike/Application.java.’ Within Eclipse right-click on ‘src/main/java’ and select ‘New->Class.’ In the ‘Package’ field, type ‘org.mike’ (or whatever package name you like) and in the ‘Name’ field ‘Application’. Click ‘Finish’. You should now see a new tab labeled ‘Application.java’ in the edit pane. Add the following code to the new Application class:

package org.mike;

import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Again, this should bring up a basic web application with a default configuration. This may be all you ever need, or you may find it necessary to have more fine grained control in which case you can add to this class to provide more detailed configuration. We will see an example of this in later chapters of this tutorial.

The heart of Spring is a somewhat mysterious object called the ‘ApplicationContext’. You will see reference to this throughout the Spring documentation. This tutorial will attempt to reduce some of the confusion you may have around this object.

The @SpringBootApplication annotation is a convenience annotation that enables several features. It enables this Application class to serve as a configuration object that will provide bean definitions to the application context, and it enables this to happen automatically. The annotation also denotes this application as a Spring MVC application, which initiates all the necessary plumbing for such an application. Additionally, component scan is enabled which scans the ‘org.mike’ package for other components, configurations and services. This supplants the need to define your beans and controllers in an external configuration file.

The entire web application is brought up with the execution of the run() method on the SpringApplication class. This starts an embedded web server and deploys the application within it exposing any endpoints defined in your application.

The functional object of our new application is the ‘Controller’. Create the following controller class under ‘src/main/java/org/mike/controllers/HelloController.java.’

package org.mike.controllers;

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;

@RestController
public class HelloController {
    @RequestMapping("/")
    public String index() {
        return "Hello World!";
    }
}

In this case the @RestController annotation is used to identify this method as a REST endpoint. The significance of this is that web requests to the ‘/’ endpoint will return data instead of a view. Had we annotated this method with @Controller, then the application would attempt to locate and resolve a view with the name ‘Hello World!’ after the ‘index’ method returns. In subsequent parts of this tutorial we will work heavily with the @Controller annotation, but it is worth noting that Spring Boot can also expose REST endpoints.

Given that we configured the application for automatic bean discovery in the ‘org.mike’ package, this controller class will be discovered when the Spring Boot application is started and wired in as a Spring MVC controller.

The @RequestMapping annotation maps the path ‘/’ to the ‘index()’ method.

1.3.3 Run it

Let’s run the application. First, let’s run the application from within Eclipse. Simply right-click on the Application.java class in the left hand Project Explorer pane and select ‘Run As->Java Application.’ In the Console tab in the bottom pane you should see several log statements ending with a line that says ‘Started Application…’

Now open a browser and type localhost:8080 in the url bar. You should see the text ‘Hello World!’ in the browser.

Some analysis of the console output is below.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.3.3.RELEASE)

2016-03-04 12:12:28.297  INFO 5784 --- [           main] org.mike.Application                     : Starting Application on broadbear-PC with PID 5784 (C:\Users\broadbear\workspace_tut\spring_boot_tutorial\bin started by broadbear in C:\Users\broadbear\workspace_tut\spring_boot_tutorial)
2016-03-04 12:12:28.300  INFO 5784 --- [           main] org.mike.Application                     : No active profile set, falling back to default profiles: default
2016-03-04 12:12:28.350  INFO 5784 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@16267862: startup date [Fri Mar 04 12:12:28 PST 2016]; root of context hierarchy
2016-03-04 12:12:28.986  INFO 5784 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2016-03-04 12:12:29.836  INFO 5784 --- [           main] e.j.JettyEmbeddedServletContainerFactory : Server initialized with port: 8080
2016-03-04 12:12:29.839  INFO 5784 --- [           main] org.eclipse.jetty.server.Server          : jetty-9.2.15.v20160210
2016-03-04 12:12:29.943  INFO 5784 --- [           main] application                              : Initializing Spring embedded WebApplicationContext
2016-03-04 12:12:29.944  INFO 5784 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1597 ms
2016-03-04 12:12:30.392  INFO 5784 --- [           main] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2016-03-04 12:12:30.395  INFO 5784 --- [           main] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'metricFilter' to: [/*]
2016-03-04 12:12:30.395  INFO 5784 --- [           main] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'characterEncodingFilter' to: [/*]
2016-03-04 12:12:30.396  INFO 5784 --- [           main] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2016-03-04 12:12:30.396  INFO 5784 --- [           main] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2016-03-04 12:12:30.396  INFO 5784 --- [           main] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'requestContextFilter' to: [/*]
2016-03-04 12:12:30.396  INFO 5784 --- [           main] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'webRequestLoggingFilter' to: [/*]
2016-03-04 12:12:30.396  INFO 5784 --- [           main] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'applicationContextIdFilter' to: [/*]
2016-03-04 12:12:30.588  INFO 5784 --- [           main] o.e.jetty.server.handler.ContextHandler  : Started o.s.b.c.e.j.JettyEmbeddedWebAppContext@f973499{/,file:/C:/Users/broadbear/AppData/Local/Temp/jetty-docbase.3392512236555004143.8080/,AVAILABLE}
2016-03-04 12:12:30.589  INFO 5784 --- [           main] org.eclipse.jetty.server.Server          : Started @3188ms
2016-03-04 12:12:30.783  INFO 5784 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@16267862: startup date [Fri Mar 04 12:12:28 PST 2016]; root of context hierarchy
2016-03-04 12:12:30.849  INFO 5784 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String org.mike.controllers.HelloController.index()
2016-03-04 12:12:30.853  INFO 5784 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2016-03-04 12:12:30.853  INFO 5784 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2016-03-04 12:12:30.884  INFO 5784 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-03-04 12:12:30.884  INFO 5784 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-03-04 12:12:30.915  INFO 5784 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-03-04 12:12:31.445  INFO 5784 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/health || /health.json],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(java.security.Principal)
2016-03-04 12:12:31.446  INFO 5784 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/env/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint.value(java.lang.String)
2016-03-04 12:12:31.447  INFO 5784 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/env || /env.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2016-03-04 12:12:31.447  INFO 5784 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/trace || /trace.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2016-03-04 12:12:31.448  INFO 5784 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/mappings || /mappings.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2016-03-04 12:12:31.448  INFO 5784 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/info || /info.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2016-03-04 12:12:31.451  INFO 5784 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/beans || /beans.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2016-03-04 12:12:31.451  INFO 5784 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/dump || /dump.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2016-03-04 12:12:31.452  INFO 5784 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/autoconfig || /autoconfig.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2016-03-04 12:12:31.452  INFO 5784 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/configprops || /configprops.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2016-03-04 12:12:31.453  INFO 5784 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/metrics/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint.value(java.lang.String)
2016-03-04 12:12:31.453  INFO 5784 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/metrics || /metrics.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2016-03-04 12:12:31.553  INFO 5784 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2016-03-04 12:12:31.562  INFO 5784 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 0
2016-03-04 12:12:31.639  INFO 5784 --- [           main] application                              : Initializing Spring FrameworkServlet 'dispatcherServlet'
2016-03-04 12:12:31.639  INFO 5784 --- [           main] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2016-03-04 12:12:31.649  INFO 5784 --- [           main] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 10 ms
2016-03-04 12:12:31.674  INFO 5784 --- [           main] o.eclipse.jetty.server.ServerConnector   : Started ServerConnector@42c28305{HTTP/1.1}{0.0.0.0:8080}
2016-03-04 12:12:31.676  INFO 5784 --- [           main] .s.b.c.e.j.JettyEmbeddedServletContainer : Jetty started on port(s) 8080 (http/1.1)
2016-03-04 12:12:31.680  INFO 5784 --- [           main] org.mike.Application                     : Started Application in 3.867 seconds (JVM running for 4.28)

From this console output we can see that the default embedded web server is Jetty. We can see all of the filter and controller mappings. We can also see several maintenance and monitoring endpoints that exposed by the application. You can try some of these endpoints yourself in the browser. Type: ‘localhost:8080/health’ to see the health status of your application. If you are using Chrome, you can add the JSONView extension that will automatically format the output of these endpoints.

Alternatively, you can start the application with Gradle Wrapper. At the comman prompt, from the ‘workspace/spring_boot_tutorial’ directory, type:

./gradlew bootRun

You will see some compile/build steps followed by similar console output seen when we ran the application from within Eclipse. You should also be able to access our new endpoint from the browser. While running the application from Eclipse is a great convenience, it is worth familiarizing yourself with the Gradle command prompt as it is a powerful and easy to use build tool, and this will help us when we need to deploy to a production server on Heroku.

1.4 Source control with Git

Source control is an integral part of any software development project and it behooves you to integrate a good source control solution into your development process. There are two primary reasons source control is so important. For one, it provides a history of your changes. This means you can check in a working set of source files and continue working without worrying about permanently breaking anything. If you decide you don’t like your changes, you can always revert to a previous check-in and undo your changes. Secondly, source control enables you to host your source files remotely. This enables you to check your source files out on different devices, and even others to access and contribute to your source files. Plus, if others are working on your source, you can see changes they have made to try to identify problems that have arisen. This guide will give you some of the basics for working with source control and future sections will continue to use source control as you would on a normal development project.

For source control, I use Git. There are a couple of Git source control hosting services that offer free repositories. Two popular Git hosing providers are GitHub and BitBucket. GitHub is great for when I want to build a community around some software, but private repositories are not free. BitBucket offers free private repositories and charges for groups of collaborators that exceed a certain number. I am not totally concerned with privacy with this code, but for the purposes of this tutorial it may be of more use to you to start using BitBucket as you may want to move on to other projects that you do not wish to make publicly available.

1.4.1 Get Git

To work with Git and BitBucket, it is necessary to install Git locally on your development workstation.

Download the git installation executable compatible with your development workstation and install it.

Once Git is installed you should be able to type ‘git’ at the command line and see a list of list of commands.

To start, you should set a couple of configuration properties in git:

$ git config --global user.name "Your Git Username"
$ git config --global user.email your.email@example.com
$ git config --global push.default current
$ git config --global alias.co checkout
$ git config --global alias.st status

The above commands include the ‘–global’ flag which means these values will be available to all repos. If you wish a configuration to be local to a repo, go to that repo’s base directory and run the ‘git config’ command without the ‘–global’ flag.

The user.name and user.email values will be used with every Git commit.

The ‘push.default current’ setting ensures only the staged changes on the branch you are currently working on will be pushed. Without this, changes on every branch would be pushed. This could get confusing, especially at first, so I am setting this to ‘current’ so only changes will be pushed on the current working branch.

The alias setting is an indispensable way to save a lot of typing. I use the status command countless times throughout the day. I usually go one step further and create a system alias, ‘gs’ or something, to avoid even having to type ‘git.’ This example just shows you how to set up a git alias. I leave further alias configuration up to the preference of the reader.

To view all of your settings you can run

git config --global --list
git config --list

The first command lists all global configurations and the second the configurations associated with the current repo.

Create a repo

To store your source files in source control, you need to set up a repository (repo). A repo is a unit of source control. You would likely have separate repos for each project. To set up a Git repo for the Spring Boot Tutorial project go to the base directory of your project, ‘workspace/spring_boot_tutorial’ and type:

git init

You will see a ‘.git’ directory appear in the base directory of your project.

Next, start tracking all the project files. You will need to create a ‘.gitignore’ file in the base directory of your project in order to avoid checking in compiled classes and the like. If you run:

git status

you will see a list of untracked files in the current repository. You should ignore everything under the /bin and /build directories, as well as some gradle and settings files. To do this, create a file, ‘.gitignore’, and add the following entries:

/build/
/bin/
/.gradle/
/.settings/

This tells Git to ignore the listed directories and all files and directories under them. Now when you run ‘git status’ you will not see these directories in the list of untracked files.

Now we need to track these files. Run:

git add -A

If you run ‘git status’ again, you will see the files have moved from an ‘Untracked’ section to a ‘Changes to be committed’ section. Git has the concept of a ‘staging’ area for files to be committed. The files you just added are now in the staging area. When you commit, all of these files will be committed.

Go ahead and commit these files by running:

git commit -m "Initial commit"

This commits all files in the staging area, and adds the provided message after the -m flag to the commit.

To see a list of your commits, type:

git log

What you have just committed is local to your workstation. You now have full source control on your local workstation, but it is not hosted remotely for sharing to multiple workstations or other team members. This is where BitBucket comes in. We will describe setting up BitBucket in the next section.

1.4.2 A drop in the BitBucket (HAR HAR HARRRRR!)

Open an account on BitBucket.

Log in and create a new repo. BitBucket does a pretty good job of guiding you through setup. For instance, after creating a repo, click ‘I have an existing project’. You will see some command line help like the following:

cd C:\Users\[user dir]\workspace\spring_boot_tutorial
git remote add origin https://[username]@bitbucket.org/[user name]/spring_boot_tutorial.git
git push -u origin --all # pushes up the repo and its refs for the first time
git push -u origin --tags # pushes up any tags

Your user name will be auto-populated. A brief explanation of the previous commands follows. In order to transfer your source code from your local workstation to your new BitBucket repository, you have to add a ‘remote’ on your local workstation. A ‘remote’ is basically a uri, or in this case a url, that identifies the remote location. Once you have added a remote, you can push your code to the BitBucket repository.

Follow this guidance to add a remote and push your code to your BitBucket repository.

1.4.3 Common git activities

1.4.3.1 Branch

git checkout -b test-branch

This creates a branch with the -b flag named ‘test-branch,’ checks out that branch, and switches to it.

1.4.3.2 Edit

touch Readme

Add explanatory text to the Readme file. Then:

git add -A

‘-A’ add all new files to the staging area.

1.4.3.3 Commit

git commit -a -m "Added readme"

‘-a’ commit all modifications to existing files. Must still add new files.

1.4.3.4 Merge

git checkout master

Switch to the master branch.

git merge test-branch

Merge the test-branch into master. There should be no conflicts.

git branch -d test-branch

Remove the branch if desired. A lot of ‘feature’ branches’ can be cleaned up afterwards. Changes are recorded in the commit and can be backed out with the commit id, so it is not necessary to keep the branch around. Sometimes it is helpful to keep a branch if it represents a release, although releases can also be tagged. To remove a branch that has not been merged into the master branch, use -D instead of -d.

1.4.3.5 Rebase

If you are like me your branch will contain several frivolous commits. It can be helpful to simplify this by ‘squashing’ the branch.

1.4.3.6 Push

git push

Now your changes are on BitBucket!

1.5 Making the application available on Heroku

Heroku is a hosting service that provides a limited free instance for users to release their applications. Though limited, it does enable you to attach whatever domain name to the service you like, so an application deployed on the free instance can be accessed just as any production application would. The free instance is mainly limited in resources such as memory and space, and the instance goes to sleep when the application is inactive. You will notice a considerable lag when opening an application you have not visited in a while. Though limited, this is a great way to put together a functioning prototype or MVP and make it available to the public.

Another aspect of Heroku is that it seems to abstract away the system aspect of deploying and hosting an application. Other hosting providers I have worked with simply provided a virtual OS instance that you could log into via ssh and configure at will. Heroku attempts to create an abstract environment driven off source control. You deploy an application on Heroku by pushing your code to Heroku. Heroku then checks out your code, builds it, and deploys it for you. This may sound strange at first but it is really quite brilliant. For one thing, Heroku provides a common environment that can be tested locally on your development workstation. You can bring up an application locally as if it were deployed on a remote Heroku instance. Another advantage is that the developer must focus on getting the build script right so some other service can properly build and deploy the application. It is all too common that developers run their applications locally, working around some development environment dependency, or worse, some individual developer’s workstation specific dependency, and discover some inconsistency after deploying to staging and or production. Heroku’s process really encourages the single-click-build concept that helps to iron out bugs introduced by environment inconsistencies.

1.5.1 Setting up the environment

Create an account on Heroku.

Download and install the Heroku toolbelt for Windows.

Run the following commands at the command line:

heroku login
heroku create

Update the build file

Heroku requires a ‘stage’ task added to the Gradle build file. This task will run by default. Add the following to gradle.build:

task stage {
  dependsOn build
}

Create a Procfile

The Procfile tells Heroku how to run your app.

Add the following to the Procfile:

web: java -Dserver.port=$PORT -jar  build/libs/spring-boot-tut-0.1.0.jar

If you are on Windows, you should create an additional Procfile.windows and change the ‘$PORT’ reference to ‘%PORT%.’

The -D arg is passed to the embedded web server by heroku to tell it what port to listen on. In order to run locally you will need to set this in the heroku config. The following commands help you do that:

heroku config - to view configs
heroku config:get PORT - to view config for specific config variable
heroku config:set PORT=[port num] - to set a config variable

1.5.2 Deploying a Spring Boot application

Run the app locally. I love this feature of Heroku. This lets you deploy your app locally so you can work out configuration problems locally instead of continually redeploying to Heroku. Type the following from the command line:

./gradlew stage
$ heroku local web

To use an alternate Procfile, in order to run on Windows for instance, add the the -f flag followed by the name of the alternate Procfile.

$ heroku local web -f Procfile.windows

To publish and run your app on Heroku, run the following commands:

git add -A
git commit -a -m "Added Procfile"
git push heroku master

If you have configured everything properly, you should see ‘BUILD SUCCESSFUL’ and ‘Verifying deploy…. done.’ There should also be a message containing the url at the bottom of the output. In my case it is ‘https://boiling-cliffs-45288.herokuapp.com/ deployed to Heroku.’ Copy paste this url into a browser, and you should see:

Hello World!

Written with StackEdit.

No comments: