Chapter 5. Getting Groovy with the Spring Boot CLI

This chapter covers

  • Automatic dependencies and imports
  • Grabbing dependencies
  • Testing CLI-based applications

Some things go really well together. Peanut butter and jelly. Abbott and Costello. Thunder and lightning. Milk and cookies. On their own, these things are great. But when paired up, they’re even more awesome.

So far, we’ve seen a lot of great things that Spring Boot has to offer, including auto-configuration and starter dependencies. When paired up with the elegance of the Groovy language, the result can be greater than the sum of its parts.

In this chapter, we’re going to look at the Spring Boot CLI, a command-line tool that brings the power of Spring Boot and Groovy together to form a simple and compelling development tool for creating Spring applications. To demonstrate the power of Spring Boot’s CLI, we’re going to rewind the reading-list application from chapter 2, rewriting it from scratch in Groovy and taking advantage of the benefits that the CLI has to offer.

5.1. Developing a Spring Boot CLI application

Most development projects that target the JVM platform are developed in the Java language and involve a build system such as Maven or Gradle to produce a deployable artifact. In fact, the reading-list application we created in chapter 2 follows this model.

The Java language has seen great improvements in recent versions. Even so, Java has a few strict rules that add noise to the code. Line-ending semicolons, class and method modifiers (such as public and private), getter and setter methods, and import statements serve a purpose in Java, but they distract from the essentials of the code. From a developer’s perspective, code noise is friction—friction when writing code and even more friction when trying to read it. If some of this code noise could be eliminated, it’d be easier to develop and read the code.

Likewise, build systems such as Maven and Gradle serve a purpose in a project. But build specifications are also one more thing that must be developed and maintained. If only there were a way to do away with the build, projects would be that much simpler.

When working with the Spring Boot CLI, there is no build specification. The code itself serves as the build specification, providing hints that guide the CLI in resolving dependencies and producing deployment artifacts. Moreover, by teaming up with Groovy, the Spring Boot CLI offers a development model that eliminates almost all code noise, producing a friction-free development experience.

In the very simplest case, writing a CLI-based application could be as easy as writing a single standalone Groovy script like the one we wrote in chapter 1. But when writing a more complete application with the CLI, it makes sense to set up a basic project structure to house the project code. That’s where we’ll get started with rewriting the reading-list application.

5.1.1. Setting up the CLI project

The first thing we’ll do is create a directory structure to house the project. Unlike Maven- and Gradle-based projects, Spring Boot CLI projects don’t have a strict project structure. In fact, the simplest Spring Boot CLI app could be a single Groovy script living in any directory in the filesystem. For the reading-list project, however, you should create a fresh, clean directory to keep the code separate from anything else you keep on your machine:

$ mkdir readinglist

Here I’ve named the directory “readinglist”, but feel free to name it however you wish. The name isn’t as important as the fact that the project will have a place to live.

We’ll need a couple of extra directories to hold the static web content and the Thymeleaf template. Within the readinglist directory, create two new directories named “static” and “templates”:

$ cd readinglist
$ mkdir static
$ mkdir templates

It’s no coincidence that these directories are named the same as the directories we created under src/main/resources in the Java-based project. Although Spring Boot doesn’t enforce a project structure like Maven and Gradle do, Spring Boot will still auto-configure a Spring ResourceHttpRequestHandler that looks for static content in a directory named “static” (among other locations). It will also still configure Thymeleaf to resolve templates from a directory named “templates”.

Speaking of static content and Thymeleaf templates, those files will be exactly the same as the ones we created in chapter 2. So that you don’t have to worry about remembering them later, go ahead and copy style.css into the static directory and readingList.html into templates.

At this point the reading-list project should be structured like this:

Now that the project is set up, we’re ready to start writing some Groovy code.

5.1.2. Eliminating code noise with Groovy

On its own, Groovy is a very elegant language. Unlike Java, Groovy doesn’t require qualifiers such as public and private. Nor does it demand semicolons at the end of each line. Moreover, thanks to Groovy’s simplified property syntax (“GroovyBeans”), the JavaBean standard accessor methods are unnecessary.

Consequently, writing the Book domain class in Groovy is extremely easy. Create a new file at the root of the reading-list project named Book.groovy and write the following Groovy class in it.

class Book {
    Long id
    String reader
    String isbn
    String title
    String author
    String description
}

As you can see, this Groovy class is a mere fraction of the size of its Java counterpart. There are no setter or getter methods, no public or private modifiers, and no semicolons. The code noise that is so common in Java is squelched, and all that’s left is what describes the essence of a book.

JDBC vs. JPA in the Spring Boot CLI

One difference you may have noticed between this Groovy implementation of Book and the Java implementation in chapter 2 is that there are no JPA annotations. That’s because this time we’re going to use Spring’s JdbcTemplate to access the database instead of Spring Data JPA.

There are a couple of very good reasons why I chose JDBC instead of JPA for this example. First, by mixing things up a little, I can show off a few more auto-configuration tricks that Spring Boot performs when working with Spring’s JdbcTemplate. But perhaps the most important reason I chose JDBC is that Spring Data JPA requires a .class file when generating on-the-fly implementations of repository interfaces. When you run Groovy scripts via the command line, the CLI compiles the scripts in memory and doesn’t produce .class files. Therefore, Spring Data JPA doesn’t work well when running scripts through the CLI.

That said, the CLI isn’t completely incompatible with Spring Data JPA. If you use the CLI’s jar command to package your application into a JAR file, the resulting JAR file will contain compiled .class files for all of your Groovy scripts. Building and running a JAR file from the CLI is a handy option when you want to deploy an application developed with the CLI, but it isn’t as convenient during development when you want to see the results of your work quickly.

Now that we’ve defined the Book domain class, let’s write the repository. First, let’s write the ReadingListRepository interface (in ReadingListRepository.groovy):

interface ReadingListRepository {

    List<Book> findByReader(String reader)

    void save(Book book)

}

Aside from a clear lack of semicolons and no public modifier on the interface, the Groovy version of ReadingListRepository isn’t much different from its Java counterpart. The most significant difference is that it doesn’t extend JpaRepository because we’re not working with Spring Data JPA in this chapter. And since we’re not using Spring Data JPA, we’re going to have to write the implementation of ReadingListRepository ourselves. The following listing shows what JdbcReadingListRepository.groovy should look like.

Listing 5.1. A Groovy and JDBC implementation of ReadingListRepository

For the most part, this is a typical JdbcTemplate-based repository implementation. It’s injected, via autowiring, with a reference to a JdbcTemplate object that it uses to query the database for books (in the findByReader() method) and to save books to the database (in the save() method).

By writing it in Groovy, we’re able to apply some Groovy idioms in the implementation. For example, in findByReader(), a Groovy closure is given as a parameter in the call to query() in place of a RowMapper implementation.[1] Also, within the closure, a new Book object is created using Groovy’s support for setting object properties at construction.

1

In fairness to Java, we can do something similar in Java 8 using lambdas (and method references).

While we’re thinking about database persistence, we also need to create a file named schema.sql that will contain the SQL necessary to create the Book table that the repository issues queries against:

create table Book (
        id identity,
        reader varchar(20) not null,
        isbn varchar(10) not null,
        title varchar(50) not null,
        author varchar(50) not null,
        description varchar(2000) not null
);

I’ll explain how schema.sql is used later. For now, just know that you need to create it at the root of the classpath (at the root directory of the project) so that there will actually be a Book table to query against.

All of the Groovy pieces are coming together, but there’s one more Groovy class we must write to make the Groovy-ified reading-list application complete. We need to write a Groovy implementation of ReadingListController to handle web requests and serve the reading list to the browser. At the root of the project, create a file named ReadingListController.groovy with the following content.

Listing 5.2. ReadingListController handles web requests for displaying and adding

Comparing this version of ReadingListController with the one from chapter 2, it’s easy to see that there’s a lot in common. The main difference, once again, is that Groovy’s syntax does away with class and method modifiers, semicolons, accessor methods, and other unnecessary code noise.

You’ll also notice that both handler methods are declared with def rather than String and both dispense with an explicit return statement. If you prefer explicit typing on the methods and explicit return statements, feel free to include them—Groovy won’t mind.

There’s one more thing we need to do before we can run the application. Create a new file named Grabs.groovy and put these three lines in it:

@Grab("h2")
@Grab("spring-boot-starter-thymeleaf")
class Grabs {}

We’ll talk more about what this class does later. For now, just know that the @Grab annotations on this class tell Groovy to fetch a few dependency libraries on the fly as the application is started.

Believe it or not, we’re ready to run the application. We’ve created a project directory, copied a stylesheet and Thymeleaf template into it, and filled it with Groovy code. All that’s left is to run it with the Spring Boot CLI (from within the project directory):

$ spring run .

After a few seconds, the application should be fully started. Open your web browser and navigate to http://localhost:8080. Assuming everything goes well, you should see the same reading-list application you saw in chapter 2.

Success! In just a few pages of this book, you’ve written a complete (albeit simple) Spring application!

At this point, however, you might be wondering how it works, considering that...

  • There’s no Spring configuration. How are the beans created and wired together? Where does the JdbcTemplate bean come from?
  • There’s no build file. Where do the library dependencies like Spring MVC and Thymeleaf come from?
  • There are no import statements. How can Groovy resolve types like JdbcTemplate and RequestMapping if there are no import statements to specify what packages they’re in?
  • We never deployed the app. Where’d the web server come from?

Indeed, the code we’ve written seems to be missing more than just a few semicolons. How does this code even work?

5.1.3. What just happened?

As you’ve probably surmised, there’s more to Spring Boot’s CLI than just a convenient means of writing Spring applications with Groovy. The Spring Boot CLI has several tricks in its repertoire, including the following:

  • The CLI is able to leverage Spring Boot auto-configuration and starter dependencies.
  • The CLI is able to detect when certain types are in use and automatically resolve the appropriate dependency libraries to support those types.
  • The CLI knows which packages several commonly used types are in and, if those types are used, adds those packages to Groovy’s default packages.
  • By applying both automatic dependency resolution and auto-configuration, the CLI can detect that it’s running a web application and automatically include an embedded web container (Tomcat by default) to serve the application.

If you think about it, these are the most important features that the CLI offers. The Groovy syntax is just a bonus!

When you run the reading-list application through the Spring Boot CLI, several things happen under the covers to make this magic work. One of the very first things the CLI does is attempt to compile the Groovy code using an embedded Groovy compiler. Without you knowing it, however, the code fails to compile due to several unknown types in the code (such as JdbcTemplate, Controller, RequestMapping, and so on).

But the CLI doesn’t give up. The CLI knows that JdbcTemplate can be added to the classpath by adding the Spring Boot JDBC starter as a dependency. It also knows that the Spring MVC types can be found by adding the Spring Boot web starter as a dependency. So it grabs those dependencies from the Maven repository (Maven Central, by default).

If the CLI were to try to recompile at this point, it would still fail because of the missing import statements. But the CLI also knows the packages of many commonly used types. Taking advantage of the ability to customize the Groovy compiler’s default package imports, the CLI adds all of the necessary packages to the Groovy compiler’s default imports list.

Now it’s time for the CLI to attempt another compile. Assuming there are no other problems outside of the CLI’s abilities (such as syntax errors or types that the CLI doesn’t know about), the code will compile cleanly and the CLI will run it via an internal bootstrap method similar to the main() method we put in Application for the Java-based example.

At this point, Spring Boot auto-configuration kicks in. It sees that Spring MVC is on the classpath (as a result of the CLI resolving the web starter), so it automatically configures the appropriate beans to support Spring MVC, as well as an embedded Tomcat bean to serve the application. It also sees that JdbcTemplate is on the classpath, so it automatically creates a JdbcTemplate bean, wiring it with a DataSource bean that was also automatically created.

Speaking of the DataSource bean, it’s just one of several other beans that are created via Spring Boot auto-configuration. Spring Boot also automatically configures beans that support Thymeleaf views in Spring MVC. This happens because we used @Grab to add H2 and Thymeleaf to the classpath, which triggers auto-configuration for an embedded H2 database and Thymeleaf.

The @Grab annotation is an easy way to add dependencies that the CLI isn’t able to automatically resolve. In spite of its ease of use, however, there’s more to this little annotation than meets the eye. Let’s take a closer look at @Grab to see what makes it tick, how the Spring Boot CLI makes it even easier by requiring only an artifact name for many commonly used dependencies, and how to configure its dependency-resolution process.

5.2. Grabbing dependencies

In the case of Spring MVC and JdbcTemplate, Groovy compilation errors triggered the Spring Boot CLI to go fetch the necessary dependencies and add them to the classpath. But what if a dependency is required but there’s no failing code to trigger automatic dependency resolution? Or what if the required dependency isn’t among the ones the CLI knows about?

In the reading-list example, we needed Thymeleaf libraries so that we could write our views using Thymeleaf templates. And we needed the H2 database library so that we could have an embedded H2 database. But because none of the Groovy code directly referenced Thymeleaf or H2 classes, there were no compilation failures to trigger them to be resolved automatically. Therefore, we had to help the CLI out a bit by adding the @Grab dependencies in the Grabs class.

Where should you place @Grab?

It’s not strictly necessary to put the @Grab annotations in a separate class as we have. They would still do their magic had we put them in ReadingListController or JdbcReadingListRepository. For organization’s sake, however, it’s useful to create an otherwise empty class definition that has all of the @Grab annotations. This makes it easy to view all of the explicitly declared library dependencies in one place.

The @Grab annotation comes from Groovy’s Grape (Groovy Adaptable Packaging Engine or Groovy Advanced Packaging Engine) facility. In a nutshell, Grape enables Groovy scripts to download dependency libraries at runtime without using a build tool like Maven or Gradle. In addition to providing the functionality behind the @Grab annotation, Grape is also used by the Spring Boot CLI to fetch dependencies deduced from the code.

Using @Grab is as simple as expressing the dependency coordinates. For example, suppose you want to add the H2 database to your project. Adding the following @Grab to one of the project’s Groovy scripts will do just that:

@Grab(group="com.h2database", module="h2", version="1.4.190")

Used this way, the group, module, and version attributes explicitly specify the dependency. Alternatively, you can express the same dependency more succinctly using a colon-separated form similar to how dependencies can be expressed in a Gradle build specification:

@Grab("com.h2database:h2:1.4.185")

These are two textbook examples of using @Grab. But the Spring Boot CLI extends @Grab in a couple of ways to make working with @Grab even easier.

First, for many dependencies it’s unnecessary to specify the version. Applying this to the example of the H2 database dependency, it’s possible to express the dependency with the following @Grab:

@Grab("com.h2database:h2")

The specific version of the dependency is determined by the version of the CLI that you’re using. In the case of Spring Boot CLI 1.3.0.RELEASE, the H2 dependency resolved will be 1.4.190.

But that’s not all. For many commonly used dependencies, it’s also possible to leave out the group ID, expressing the dependency by only giving the module ID to @Grab. This is what enabled us to express the following @Grab for H2 in the previous section:

@Grab("h2")

How can you know which dependencies require a group ID and version and which you can grab using only the module ID? I’ve included a complete list of all the dependencies the Spring Boot CLI knows about in appendix D. But generally speaking, it’s easy enough to try @Grab dependencies with only a module ID first and then only express the group ID and version if the module ID alone doesn’t work.

Although it’s very convenient to express dependencies giving only their module IDs, what if you disagree with the version chosen by Spring Boot? What if one of Spring Boot’s starters transitively pulls in a certain version of a library, but you’d prefer to use a newer version that contains a bug fix?

5.2.1. Overriding default dependency versions

Spring Boot brings a new @GrabMetadata annotation that can be used with @Grab to override the default dependency versions in a properties file.

To use @GrabMetadata, add it to one of the Groovy script files giving it the coordinates for a properties file with the overriding dependency metadata:

@GrabMetadata("com.myorg:custom-versions:1.0.0")

This will load a properties file named custom-versions.properties from a Maven repository in the com/myorg directory. Each line in the properties file should have a group ID and module ID as the key, and the version as the value. For example, to override the default version for H2 with 1.4.186, you can point @GrabMetadata at a properties file containing the following line:

com.h2database:h2=1.4.186
Using the Spring IO platform

One way you might want to use @GrabMetadata is to work with dependency versions defined in the Spring IO platform (http://platform.spring.io/platform/). The Spring IO platform offers a curated set of dependencies and versions aimed to give confidence in knowing which versions of Spring and other libraries will work well together. The dependencies and versions specified by the Spring IO platform is a superset of Spring Boot’s set of known dependency libraries, and it includes several third-party libraries that are frequently used in Spring applications.

If you’d like to build Spring Boot CLI applications on the Spring IO platform, you’ll just need to annotate one of your Groovy scripts with the following @GrabMetadata:

@GrabMetadata('io.spring.platform:platform-versions:1.0.4.RELEASE')

This overrides the CLI’s set of default dependency versions with those defined by the Spring IO platform.

One question you might have is where Grape fetches all of its dependencies from? And is that configurable? Let’s see how you can manage the set of repositories that Grape draws dependencies from.

5.2.2. Adding dependency repositories

By default, @Grab-declared dependencies are fetched from the Maven Central repository (http://repo1.maven.org/maven2/). In addition, Spring Boot also registers Spring’s milestone and snapshot repositories to be able to fetch pre-released dependencies for Spring projects. For many projects, this is perfectly sufficient. But what if your project needs a library that isn’t in Central or the Spring repositories? Or what if you’re working within a corporate firewall and must use an internal repository?

No problem. The @GrabResolver annotation enables you to specify additional repositories from which dependencies can be fetched.

For example, suppose you want to use the latest Hibernate release. Recent Hibernate releases can only be found in the JBoss repository, so you’ll need to add that repository via @GrabResolver:

@GrabResolver(name='jboss', root=
  'https://repository.jboss.org/nexus/content/groups/public-jboss')

Here the resolver is named “jboss” with the name attribute. The URL to the repository is specified in the root attribute.

You’ve seen how Spring Boot’s CLI compiles your code and automatically resolves several known dependency libraries as needed. And with support for @Grab to resolve any dependencies that the CLI isn’t able to resolve automatically, CLI-based applications have no need for a Maven or Gradle build specification (as is required by traditionally developed Java applications). But resolving dependencies and compiling code aren’t the only things that build processes do. Project builds also usually execute automated tests. If there’s no build specification, how do the tests run?

5.3. Running tests with the CLI

Tests are an important part of any software project, and they aren’t overlooked by the Spring Boot CLI. Because CLI-based applications don’t involve a traditional build system, the CLI offers a test command for running tests.

Before you can try out the test command, you need to write a test. Tests can reside anywhere in the project, but I recommend keeping them separate from the main components of the project by putting them in a subdirectory. You can name the subdirectory anything you want, but I chose to name it “tests”:

$ mkdir tests

Within the tests directory, create a new Groovy script named ReadingListControllerTest.groovy and write a test for the ReadingListController. To get started, listing 5.3 has a single test method for testing that the controller handles HTTP GET requests properly.

Listing 5.3. A Groovy test for ReadingListController

As you can see, this is a simple JUnit test that uses Spring’s support for mock MVC testing to fire a GET request at the controller. It starts by setting up a mock implementation of ReadingListRepository that will return a single-entry list of Book. Then it creates an instance of ReadingListController, injecting the mock repository into the readingListRepository property. Finally, it sets up a MockMvc object, performs a GET request, and asserts expectations with regard to the view name and model contents.

But the specifics of the test aren’t as important here as how you run the test. Using the CLI’s test command, you can execute the test from the command line like this:

$ spring test tests/ReadingListControllerTest.groovy

In this case, I’m explicitly selecting ReadingListControllerTest as the test to run. If you have several tests within the tests/ directory and want to run them all, you can give the directory name to the test command:

$ spring test tests

If you’re inclined to write Spock specifications instead of JUnit tests, you may be pleased to know that the CLI’s test command can also execute Spock specifications, as demonstrated by ReadingListControllerSpec in the following listing.

Listing 5.4. A Spock specification to test ReadingListController

ReadingListControllerSpec is a simple translation of ReadingListControllerTest from a JUnit test into a Spock specification. As you can see, its one test very plainly states that when a GET request is performed against “/”, then the response should have a view named readingList and the expected list of books should be in the model at the key books.

Even though it’s a Spock specification, ReadingListControllerSpec can be run with spring test tests the same way as a JUnit-based test.

Once the code is written and the tests are all passing, you might want to deploy your project. Let’s see how the Spring Boot CLI can help produce a deployable artifact.

5.4. Creating a deployable artifact

In conventional Java projects based on Maven or Gradle, the build system is responsible for producing a deployment unit; typically a JAR file or a WAR file. With Spring Boot CLI, however, we’ve simply been running our application from the command line with the spring command.

Does that mean that if you want to deploy a Spring Boot CLI application you must install the CLI on your server and fire up the application manually from the command line? That seems awfully inconvenient (not to mention risky) when deploying to production environments.

We’ll talk more about options for deploying Spring Boot applications in chapter 8. For now, though, let me show you one more trick that the CLI has up its sleeve. From within the CLI-based reading-list application, issue the following at the command line:

$ spring jar ReadingList.jar .

This will package up the entire project, including all dependencies, Groovy, and an embedded Tomcat, into a single executable JAR file. Once complete, you’ll be able to run the app at the command line (without the CLI) like this:

$ java -jar ReadingList.jar

In addition to being run at the command line, the executable JAR can be deployed to several Platform-as-a-Service (PaaS) cloud platforms including Pivotal Cloud Foundry and Heroku. You’ll see how in chapter 8.

5.5. Summary

The Spring Boot CLI takes the simplicity offered by Spring Boot auto-configuration and starter dependencies and turns it up a notch. Using the elegance of the Groovy language, the CLI makes it possible to develop Spring applications with minimal code noise.

In this chapter we completely rewrote the reading-list application from chapter 2. But this time we developed it in Groovy as a Spring Boot CLI application. You saw how the CLI makes Groovy even more elegant by automatically adding import statements for many commonly used packages and types. And the CLI is also able to automatically resolve several dependency libraries.

For libraries that the CLI is unable to automatically resolve, CLI-based applications can take advantage of the Grape @Grab annotation to explicitly declare dependencies without a build specification. Spring Boot’s CLI extends @Grab so that, for many commonly needed library dependencies, you only need to declare the module ID.

Finally, you also saw how to execute tests and build deployable artifacts, tasks commonly handled by build systems, with the Spring Boot CLI.

Spring Boot and Groovy go well together, each boosting the other’s simplicity. We’re going to take another look at how Spring Boot and Groovy play well together in the next chapter as we explore how Spring Boot is at the core of the latest version of Grails.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset