Skip to main content

Introduction to Spring Roo Shell

Spring Roo is available as a CLI shell-style application and as an STS plugin. Since I'm not a fan of the Eclipse IDE (no offense and I don't need to debate it), the focus will be on using the CLI. The first step is to download the latest release from the Spring Roo project homepage.

Before getting too far into it, it will be helpful to understand some basics about the Roo Shell (RS) application. The startup script can be used to bootstrap RS commands or to simply launch the shell program when no commands are included. The commands are applied relative to the current working directory, so be prepared for that. The recommended approach is to unpack the Spring Roo distribution to its own folder, add its /bin directory to the path, and execute RS from your project directories. This will become more obvious in the examples below.

To keep it simple, I've created a directory under my Linux home directory and unpacked the distro:

~ $ mkdir spring-roo
~ $ cd spring-roo/
~/spring-roo $ wget http://spring-roo-repository.springsource.org.s3.amazonaws.com/release/ROO/spring-roo-2.0.0.RELEASE.zip
~/spring-roo $ unzip spring-roo-2.0.0.RELEASE.zip

Now, I create a project directory called roo-demo and start the RS from within it.


~ $ cd projects/
~/projects $ mkdir roo-demo
~/projects $ cd roo-demo/
~/projects/roo-demo $ ~/spring-roo/spring-roo-2.0.0.RELEASE/bin/roo.sh
The Roo Shell is built on top of the Felix OSGi container. Fortunately, I am very familiar with OSGi having built several very significant system using this technology. It is a great choice for being able to install and execute dynamic functionality and Spring Roo makes good use of this. Keep in mind that neither OSGi nor the Roo libraries are necessary in your Spring applications. The RS is just a mechanism to provide the scaffolding tools.

Pressing the TAB key brings up a short list of commands, while typing 'help' brings a long list of commands with comprehensive details. Type 'help <command name>' for even more details.

If no project exists in your working directory, typing 'hint' will give you a starting point.

roo> hint

So, I'll following the advice of hint and the 'project setup' documentation and type the following:

roo> project setup --topLevelPackage com.tyler.roo.demo --java 8
Created ROOT/pom.xml
Created SRC_MAIN_RESOURCES
Created SRC_MAIN_RESOURCES/application-dev.properties
Updated ROOT/pom.xml [added property 'java.version' = '1.8']
Created SRC_MAIN_JAVA/com/tyler/roo/demo
Created SRC_MAIN_JAVA/com/tyler/roo/demo/DemoApplication.java
Created SRC_MAIN_RESOURCES/banner.txt

Now my working directory has application files and I will examine them. The POM file is different from that generated by the Spring Boot Starter, so this may be one of the early changes to address. This is particularly important due to the different POM setup between a Spring Boot and a Spring Cloud application. Roo doesn't seem to discriminate between the two and I believe it's important.

A quick review of the generated files reveals some standard boilerplate items for a Spring Boot application that includes a DemoApplication.java and application-dev.properties file.

I'm going to run the project at this point to see what happens.

~/projects/roo-demo $ mvn clean install spring-boot:run

As expected, the Spring Boot container started, did a few things, then shutdown.

Going back to the 'hint' command is RS, it now suggests adding JPA configuration. Now, JPA isn't the only game in town when it comes to Cloud Native persistence, but it's what Roo offers now. This is another area of future focus, like instrumenting for various NoSQL technologies.

The 'jpa setup' command has many options, but I will start with the basic in-memory configuration to see what happens. Naturally, I'd like to see Roo follow the more Cloud Native patterns available in Spring Boot/Cloud, but maybe I'm making assumptions early. Let's see what happens:

roo> jpa setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY
Created SRC_MAIN_RESOURCES/application.properties
Updated SRC_MAIN_RESOURCES/application.properties
Updated SRC_MAIN_RESOURCES/application-dev.properties
Updated ROOT/pom.xml [added dependencies org.springframework.boot:spring-boot-starter-data-jpa:, org.springframework.boot:spring-boot-starter-jdbc:, org.hsqldb:hsqldb:; added property 'springlets.version' = '1.2.0.RELEASE'; added dependencies io.springlets:springlets-data-jpa:${springlets.version}, io.springlets:springlets-data-jpa:${springlets.version}; added dependencies io.springlets:springlets-data-commons:${springlets.version}, io.springlets:springlets-data-commons:${springlets.version}]

As expected from a scaffolding mechanism, RS created and updated existing resources. What is interesting is the addition of the Springlets technology, which provides extensions to typical Spring libraries. However, much of the functionality wired in by the current RS add-ons utilizing Springlets are available following typical Spring Boot/Cloud patterns, so this is another area of focus for future improvement to the scaffolding behavior.

Going to run the application again to see what happens:

~/projects/roo-demo $ mvn clean install spring-boot:run

Nothing unexpected here. The Spring Boot container logged additional actions relating to bootstrapping the JPA context. Another thing I noticed with regards to the Spring Boot JAR artifact is that the POM allows it to produce the default name. This will need to be changed to support deployment into containers.

In addition to adding the HSQL libraries, it also configured the Spring Data properties to use the in-memory DB. These properties are suitable for dev/test cycles, but would be overridden by environment settings to bind the application to production-like DB. I wonder if the Spring Data configuration can be changed if I run the 'jpa setup' command again with a different DB:

roo> jpa setup --provider HIBERNATE --database MYSQL --force
Updated SRC_MAIN_RESOURCES/application.properties
Updated ROOT/pom.xml [removed dependency org.hsqldb:hsqldb:; added dependency mysql:mysql-connector-java:; skipped dependencies org.springframework.boot:spring-boot-starter-data-jpa:, org.springframework.boot:spring-boot-starter-jdbc:; skipped dependency io.springlets:springlets-data-jpa:${springlets.version}; skipped dependency io.springlets:springlets-data-commons:${springlets.version}]

It removed the HSQL dependencies and added the MYSQL dependencies as well as changed the Spring Data configuration. Using other 'jpa setup' parameters in conjunction with the 'property add' or 'property remove' commands could correctly configure the JPA environment in a way suitable for both dev/test and production. I am thinking ahead in terms of using Docker or PCF-dev to execute the application, but we also want to run unit and integration tests during the build process.

Switching back to HSQL:

roo> jpa setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY --force
Updated SRC_MAIN_RESOURCES/application.properties
Updated ROOT/pom.xml [removed dependency mysql:mysql-connector-java:; added dependency org.hsqldb:hsqldb:; skipped dependencies org.springframework.boot:spring-boot-starter-data-jpa:, org.springframework.boot:spring-boot-starter-jdbc:; skipped dependency io.springlets:springlets-data-jpa:${springlets.version}; skipped dependency io.springlets:springlets-data-commons:${springlets.version}]

...and returning to the 'hint' command, RS is telling me it's time to add entities. Using the following command to create a Customer entity:

roo> entity jpa --class ~.model.Customer --entityFormatExpression "#{firstName} #{lastName}"
Created SRC_MAIN_JAVA/com/tyler/roo/demo/model
Created SRC_MAIN_JAVA/com/tyler/roo/demo/model/Customer.java
Updated SRC_MAIN_JAVA/com/tyler/roo/demo/model/Customer.java
Updated ROOT/pom.xml [added dependencies io.springlets:springlets-context:${springlets.version}, io.springlets:springlets-context:${springlets.version}]
Created SRC_MAIN_JAVA/com/tyler/roo/demo/model/Customer_Roo_Jpa_Entity.aj
Created SRC_MAIN_JAVA/com/tyler/roo/demo/model/Customer_Roo_JavaBean.aj

The result is a number of additional resources added to the project. The Customer class has a bunch of Roo based annotations that I don't yet understand. I'm sure there's some usefulness to it, but I'd rather see standard annotations with some Roo type interceptors configured into the Spring Context. This, again, would produce more consistent source files that follow typical Spring Boot patterns. Spring Roo claims up-front that no Roo runtime libraries are necessary, but this isn't true. There's still much investigation to conduct regarding the inner workings of Spring Roo, so I'm proposing throwing the baby out with the bath water. I already knew this going in to this deep dive.

Running the application again:

~/projects/roo-demo $ mvn clean install spring-boot:run

...reveals a failure to start. The runtime claims an implementation of the Bean Validation API is not available in the runtime. Why has this happened?

Before getting to far into the weeds, perhaps I've jumped the gun on running the app. I'll add some fields to the Customer entity and try again:

roo> field string --class ~.model.Customer --fieldName firstName
Updated SRC_MAIN_JAVA/com/tyler/roo/demo/model/Customer.java
Updated SRC_MAIN_JAVA/com/tyler/roo/demo/model/Customer_Roo_JavaBean.aj

~.model.Customer roo> field string --fieldName lastName
Updated SRC_MAIN_JAVA/com/tyler/roo/demo/model/Customer.java
Updated SRC_MAIN_JAVA/com/tyler/roo/demo/model/Customer_Roo_JavaBean.aj

One interest of note is the RS prompt and how it indicates the class context.

Now, try to run it:

~/projects/roo-demo $ mvn clean install spring-boot:run

Same problem! Now for some deep dive... First, generate repositories and services:

~.model.Customer roo> repository jpa --all
Created SRC_MAIN_JAVA/com/tyler/roo/demo/config
Created SRC_MAIN_JAVA/com/tyler/roo/demo/config/SpringDataJpaDetachableRepositoryConfiguration.java
Created SRC_MAIN_JAVA/com/tyler/roo/demo/repository
Created SRC_MAIN_JAVA/com/tyler/roo/demo/repository/CustomerRepository.java
Created SRC_MAIN_JAVA/com/tyler/roo/demo/repository/CustomerRepositoryCustom.java
Created SRC_MAIN_JAVA/com/tyler/roo/demo/repository/CustomerRepositoryImpl.java
Updated ROOT/pom.xml [added dependency com.querydsl:querydsl-jpa:; added dependencies org.springframework.roo:org.springframework.roo.querydsl.processor:2.0.0.RELEASE, org.springframework.roo:org.springframework.roo.querydsl.processor:2.0.0.RELEASE; added plugins com.mysema.maven:apt-maven-plugin:1.1.3, com.mysema.maven:apt-maven-plugin:1.1.3]
Created SRC_MAIN_JAVA/com/tyler/roo/demo/config/SpringDataJpaDetachableRepositoryConfiguration_Roo_Jpa_Repository_Configuration.aj
Created SRC_MAIN_JAVA/com/tyler/roo/demo/repository/CustomerRepository_Roo_Jpa_Repository.aj
Created SRC_MAIN_JAVA/com/tyler/roo/demo/repository/CustomerRepositoryCustom_Roo_Jpa_Repository_Custom.aj
Created SRC_MAIN_JAVA/com/tyler/roo/demo/repository/CustomerRepositoryImpl_Roo_Jpa_Repository_Impl.aj

~.model.Customer roo> service --all
Created SRC_MAIN_JAVA/com/tyler/roo/demo/service/api
Created SRC_MAIN_JAVA/com/tyler/roo/demo/service/api/CustomerService.java
Created SRC_MAIN_JAVA/com/tyler/roo/demo/service/impl
Created SRC_MAIN_JAVA/com/tyler/roo/demo/service/impl/CustomerServiceImpl.java
Created SRC_MAIN_JAVA/com/tyler/roo/demo/service/api/CustomerService_Roo_Service.aj
Created SRC_MAIN_JAVA/com/tyler/roo/demo/service/impl/CustomerServiceImpl_Roo_Service_Impl.aj

Lot's of cool new stuff but no joy on the Bean Validation provider. I could always just add it manually, but what's the point of that. I'll follow the next 'hint' which is to add Spring MVC:

roo> web mvc setup
Created SRC_MAIN_JAVA/com/tyler/roo/demo/config/WebMvcConfiguration.java
Created SRC_MAIN_JAVA/com/tyler/roo/demo/config/jackson
Created SRC_MAIN_JAVA/com/tyler/roo/demo/config/jackson/DomainModelModule.java
Updated SRC_MAIN_RESOURCES/application-dev.properties
Updated ROOT/pom.xml [added dependency org.springframework.boot:spring-boot-starter-web:null; added dependency joda-time:joda-time:null; added property 'tracee.version' = '1.1.2'; added dependencies io.tracee.binding:tracee-springmvc:${tracee.version}, io.tracee.binding:tracee-springmvc:${tracee.version}; added dependencies io.springlets:springlets-boot-starter-web:${springlets.version}, io.springlets:springlets-boot-starter-web:${springlets.version}]
Created SRC_MAIN_JAVA/com/tyler/roo/demo/config/WebMvcConfiguration_Roo_WebMvcConfiguration.aj
Created SRC_MAIN_JAVA/com/tyler/roo/demo/config/jackson/DomainModelModule_Roo_DomainModelModule.aj

I don't see anything about a validator in those changes, but what the heck, I'll try to run it again...

That seemed to do the trick. Somewhere in the hundreds of additional libraries downloaded to support Spring MVC, a bean validation provider got included. I don't like that it requires the MVC stuff in order to satisfy that requirement, so this is another point to address.

Before moving on, a review of all the additional class created from the last couple of commands doesn't reveal much more than boilerplate code and additional Roo annotations. This is definitely and area of further focus. For now, I just want to create some RESTful endpoints with which I can create and query customers.

Supposedly the following command will generate REST controllers for the Customer entity:

roo> web mvc controller --all
Created SRC_MAIN_JAVA/com/tyler/roo/demo/web
Created SRC_MAIN_JAVA/com/tyler/roo/demo/web/CustomersCollectionJsonController.java
Created SRC_MAIN_JAVA/com/tyler/roo/demo/web/CustomersItemJsonController.java
Created SRC_MAIN_JAVA/com/tyler/roo/demo/web/CustomerDeserializer.java
Created SRC_MAIN_JAVA/com/tyler/roo/demo/web/CustomerJsonMixin.java
Updated SRC_MAIN_JAVA/com/tyler/roo/demo/config/jackson/DomainModelModule_Roo_DomainModelModule.aj
Created SRC_MAIN_JAVA/com/tyler/roo/demo/web/CustomerDeserializer_Roo_EntityDeserializer.aj
Created SRC_MAIN_JAVA/com/tyler/roo/demo/web/CustomersCollectionJsonController_Roo_JSON.aj
Created SRC_MAIN_JAVA/com/tyler/roo/demo/web/CustomersItemJsonController_Roo_JSON.aj

And a quick runtime test before reviewing the code... Logs reveal a number of REST endpoints mapped to '/customers' following typical RESTful patterns using GET, POST, PUT, and DELETE methods. There are also endpoints under '/customers/batch' similarly configured for what I assume are operations over collections of Customer entities. Once again, however, the controllers utilize Roo annotations rather than Spring annotations, so the dependency continues on Roo. The generated .java class files don't contain code, but they are annotated with various Roo annotations. Each .java file has a corresponding .aj file that does include code that is generated by some Aspectj library. This allows the ability to push code into the .java files manually while still retaining the auto-generated scaffolding. However, this does not support the ultimate goal of comprehensive application generation from an application model descriptor.

Time to break out Postman for some RESTful actions. Try to retrieve all the Customers:

GET /customers HTTP/1.1
Host: localhost:8080
Accept: application/json
Cache-Control: no-cache
Postman-Token: 2daa62e9-52f3-ab5f-d013-a9bbaf49fef6

Returns:

{
    "content": [],
    "last": true,
    "totalPages": 0,
    "totalElements": 0,
    "sort": null,
    "first": true,
    "numberOfElements": 0,
    "size": 20,
    "number": 0
}

No Customers, as expected. So, let's add a Customer:

POST /customers HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 044d3f92-a01f-1eed-4bf0-a4dfb04a216a

{
"firstName" : "Matthew",
"lastName" : "Tyler"
}

Results in a '201 Created' response and querying all customers gets:

{
    "content": [
        {
            "id": 1,
            "version": 0,
            "firstName": "Matthew",
            "lastName": "Tyler"
        }
    ],
    "last": true,
    "totalPages": 1,
    "totalElements": 1,
    "sort": null,
    "first": true,
    "numberOfElements": 1,
    "size": 20,
    "number": 0
}

Conclusion

That's all I wanted to achieve in this session. I wanted to figure out the least amount of work I needed to perform in order to create a RESTful service to manager Customers. Here the commands to accomplish this task:

project setup --topLevelPackage com.tyler.roo.demo --java 8
jpa setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY
entity jpa --class ~.model.Customer --entityFormatExpression "#{firstName} #{lastName}"
field string --fieldName firstName
field string --fieldName lastName
repository jpa --all
service --all
web mvc setup
web mvc controller --all
I can take all these commands and place them into a single text file and execute that text file using the 'script <filename>' command in the RS. I can also execute the 'script' command from the OS command line by running the Roo shell script followed by the command:

roo> script demo.roo

or

~/spring-roo/spring-roo-2.0.0.RELEASE/bin/roo.sh "script demo.roo"

Overall, I find the scaffolding provided by Roo pretty cool. I will need to determine why Roo chose to generate code the way it does and this may require a deeper dive into AspectJ.

Comments

Popular posts from this blog

Astronauts?

Something happened today that me disturbed. I didn't notice this "thing" that has been occurring over the past two months, but had I, I'd have spoken out sooner. Today, Blue Origin sent another group of tourists into space for a short time. The headliner was William Shatner, a cultural icon. I'm happy for him and the rest of the group for being able to have the experience. This isn't the thing that's bothering me. On their descent back to earth, the mission control commander dubbed them America's newest astronauts, designating them an incremental astronaut number somewhere in the 500's. Astronauts? If what they did, which was to sit in a seat, qualifies them as astronauts, then everyone flying as passengers on an airliner should be anointed "pilots". Correct me if I'm wrong, but astronauts spend decades learning and practicing science, engineering, or aeronautics before applying to and being accepted into one of the most rigorous prog

Why Couldn't N7022G Establish on the Localizer?

Why did Dr. Das have trouble establishing on the localizer for 28R? He followed his vector to the localizer but started to veer right of the signal when he neared the field. The localizer signal would have been  Some panels are equiped with a "reverse" switch between the radio navigator and the VOR or HSI instrument. The reverse switch transforms the signal from the VHF navigation receiver so that indicator needles appear as they would in a forward approach and the pilot wouldn't have to translate (or reverse interpret) the needle. So, if equiped with a reverse switch and the switch was inadvertently in the reverse position, veering right on the localizer would look like the correct action for the pilot to make. He also may have been thinking more about circling around to runway 23 and that's why he veered right early. Though he was reminded plenty of times of what he approach was. The controller reported Dr. Das was veering right of the track. The pilot acknowledge i

The Importance of Television

I have no intention of pontificating on the impact of television on humankind. Before TV, people went to movie theaters to get motion pictures from around the world. Before that, they attended plays and other theatrical productions. Before that, they played with sticks and rocks (known today as golf). Enough on that. Last Sunday, after Dawn and I were fortunate enough to watch Donald Trump speak at CPAC, Life, Liberty, and Levin, and Steve Hilton, our living room TV died. It wasn't necessarily unexpected as it was exhibiting turmoil prior to its final Swan Song. But, still, dissappointing. The TV was purchased six years ago at Costco. Neither Dawn nor I were happy with a TV that only lasted six years. If it made it to ten years, then that would be acceptable. Nonetheless, it's time for another television and defining the television's mission given what's now know. In particular, with regards to mission, the living room TV must not have a screen glare. The windows on the