spring-boot

@Cachable usage and gotchas

Recently I was working with a colleague to implement caching in an application. Initially it looks fairly straight forward to integrate it, however, because of the way the code was being called we came across a number of problems that I want to share.

To see the code examples of where @Cachable works and where it doesn’t work clone the code from https://github.com/marcthomas2013/ehcache

Dependencies required

This example code is based on Spring Boot 1.2.5, however, when using Spring Boot and ehcache prior to Spring Boot 1.3 you will need to pull in the following dependency to be able to get the ehcacheCacheManager. I struggled to find the dependency that I needed for org.springframework.cache.ehcache.EhCacheCacheManager so, here it is.

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context-support</artifactId>
   <version>4.2.0.RELEASE</version>
</dependency>

Cachable annotation

Once you have the ehcacheCacheManager set up and an ehcache.xml config file in your resources folder then you are ready to start adding the @Cachable annotation to your public methods.

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
         monitoring="autodetect" dynamicConfig="true">

    <!-- <diskStore path="java.io.tmpdir" /> -->
    <diskStore path="~/cache" />

    <cache name="demoCache"
           maxEntriesLocalHeap="10000"
           maxEntriesLocalDisk="1000"
           eternal="false"
           diskSpoolBufferSizeMB="20"
           timeToIdleSeconds="300" timeToLiveSeconds="600"
           memoryStoreEvictionPolicy="LFU"
           transactionalMode="off">
        <persistence strategy="localTempSwap" />
    </cache>
</ehcache>

In the example code we have a cache setup in ehcache.xml with the name “demoCache” this name needs to go into the value parameter. Then the key that you want to use for the cache is set in the “key” field. This key field can be defined as something more than just a parameter name, see Spring’s documentation for more detail.

@Cacheable(value="demoCache", key="#url")
public String getURLResponse(String url) {
    System.out.println("This should only happen once!");
    return "Success";
}

 

Different ways of invoking the cache method

Some ways work and some don’t. In the example code referred to above there is a class MyController that invokes all the cache calls.

In the MyService class the getURLResponse method contains a log line that says “This should only happen once!” this is to show when the method is invoked. If the caching is working then you should only ever see this line once because the cache should be returning the value, however, there are a number of demonstration method calls that show when this doesn’t work and this article explains why.

callServiceDirect explanation

The first call callServiceDirect in MyController is probably the most likely scenario. A controller calling an autowired service method. You’ll see in the log that the target method is called once, and then the response is cached. The second call return value is returned straight from the cache.

callFromObjectWithDependencyPassedIn explanation

The next call callFromObjectWithDependencyPassedIn shows the scenario if you have another class that needs to invoke the service method and you need to pass that dependency in. This approach works and is the way you should do it, but then we’ll compare it with the next method callFromObjectCreatedByTheService that doesn’t work.

callFromObjectCreatedByTheService explanation

There is a method called callFromObjectCreatedByTheService that is very quirky but is used to highlight a Spring Aspect Oriented Programming (AOP) proxy issue that people could get caught out with. When the code is run you will notice that the log shows that the methods @Cachable annotation isn’t invoked. To explain why this is we need to look into the createObjectThatCallsService method in MyService.

What this method is doing is creating another object and passing itself into the object as a form of dependency injection. You might think that this is fine however, it doesn’t work and it is because Spring needs to Proxy the objects that it manages. The Spring Documentation describes this in detail but essentially proxying means that outside of the MyService class references to the object are in fact a Spring Proxy to the MyService object, whereas when you are running code within MyService the object’s this will be the MyService and not the Spring Proxy.

For AOP based annotations like @Cachable to work they need to be invoked using the proxied object and not the directly the object where the method exists. Effectively under the hood when a method with @Cachable is called using a proxied object Spring can then invoke any annotation based calls prior to the actual method being invoked. This is not possible if you are calling MyService direct. This is why this approach doesn’t work.

callingPrivateCacheMethodFromWithinService explanation

callingPrivateCacheMethodFromWithinService is another example of a call to an @Cachable method that doesn’t work and is for the same reason as explained above but for a different use case. The Spring Documentation on caching annotations explains on a tip box on the right with the title “Method visibility and @Cacheable/@CachePut/@CacheEvict” that this won’t work because of a limitation of Spring AOP and it suggests if you want to be able to call private methods internally with AOP based annotations then you will need to setup AspectJ instead. Sadly, I haven’t been able to find a good tutorial on setting up AspectJ instead of Spring AOP.

callingCacheMethodFromWithinService explanation

callingCacheMethodFromWithinService is another example of a call to an @Cachable method that doesn’t work and is for the same reason as explained above but for a different use case. You will notice that this is the is calling the same public method that works for all calls that are external to MyService and as long as they are invoked using the Spring Proxied object they will work, however, this example is calling the @Cachable method inside the MyService which therefore won’t call the method via the proxy object.

How to create a Spring Boot Remote Shell Command

So today I’ve been looking at the actuator and remote functionality in Spring Boot and thought I’d have a go and getting a custom command working in my simple Spring Boot app.

One challenge from the documentation is it only talks about how to setup the command using Groovy, so what if you are using Java? There are a few pitfalls that catch people out so I thought I’d document them here in one article.

So, the documentation on the remote shell does say that the command will be looked for on the class path at /commands or /crash/commands the subtle point about this is the command should be put in your project so that they are built because the remote shell command are compiled on first use so they need to be in your /resources folder.

So, you should put the source code for any remote shell commands in /src/main/resources/commands/ the downside with this is now that it is in the resources folder the IDE won’t highlight any syntax issues. My tip would be to write the command in your java source tree and then when you are happy with it, drag it into the resources folder. One reason for this is any compilation issues will be ignored when invoking the command.

Dependency required:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-remote-shell</artifactId>
</dependency>

Two annotations you’ll need on your command are:

@Command

@Usage(“My Test Command”)

These two annotations indicate that this is a shell command and that the command description is “My Test Command”.

The name of your class becomes the command name.

See my example source code below, that was in my Spring Boot Project at /src/main/resources/commands/

 

package commands;
import org.crsh.cli.Command;
import org.crsh.cli.Usage;
import org.crsh.command.BaseCommand;
public class mycommand extends BaseCommand {
    @Command
    @Usage("My Test Command")
    public String main() {
        return "test command invoked";
    }
}

How to make a Spring Boot Jar into a War to deploy on tomcat

Spring boot is a brilliant addition to the Spring Framework however, not everyone wants to use the Jar format that it encourages you to use. Searching on the internet the information is there to switch from Jar to War however, it doesn’t seem to be in one place in the documentation so I thought I’d put it together as a single post.

If you create a WAR project straight away then the following will be provided, however, if you have started off with a Jar packaging type this article will explain how to switch it to war. pom.xml changes The first thing you will need to do is change your pom.xml file. Change <packaging> value from jar to war. Add the following to tell the project that the tomcat server is provided.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-tomcat</artifactId>
  <scope>provided</scope>
</dependency>

Code changes In your Spring Boot application class that has the @SpringBootApplication annotation you need to extend SpringBootServletInitializer and override the configure method. e.g.

@SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer{
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(DemoApplication.class);
    }
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

So now you should be able to deploy this war file to your application server, e.g. tomcat. Also, you will see in your target folder there are two war files, one with .original extension. This is basically because the Spring Boot maven plugin has built the war file such that it can still be run using java. e.g. java -jar demo-0.0.1-SNAPSHOT.war Hope this helps

How to deploy a Spring Boot application to Amazon AWS using Elastic Beanstalk

I have recently started playing with Spring Boot and have been really impressed with everything I’ve seen. If I need to create a RESTful web service, no problem. If I need to create a web MVC application that uses JPA, no problem. Using embedded Tomcat and H2 database out of the box enables any Java developer to rapidly create applications. Then when I need to deploy this on to a Tomcat server as a war it’s simply a case of just changing the pom.xml and ensuring that the tomcat dependencies are provided. (I’ll cover this later).

Summary
This article is the first in a series that will share what I learn about Spring Boot and deploying it into the cloud. The articles will include details on how to architect an application that uses Spring Profiles to run integration tests on local databases, while being able to deploy the same code onto a production server in the cloud.

Watch the YouTube video below that covers all of the steps in this article.

Resources
The code that goes along side this article can be found here

Resources that I have used to build my understanding of Spring Boot and Cloud deployment include:

– Rob Harrop’s demo of Spring deployment on Amazon AWS. There are some excellent tips on Amazon AWS and what it does well and not so well. It’s well worth a watch.

– Spring Guides available here. All the guides you need to get up and running with the basics.

Creating a Spring Boot War
1) Start up eclipse IDE with the Spring Extensions installed. For Luna add this link to your update installer http://dist.springsource.com/release/TOOLS/update/e4.4/
2) You’ll also need Tomcat server installed in Eclipse. If you don’t have this setup then search Google for setup instructions before you continue.
3) Select File->New->Other->Spring->Spring Starter Project
4) Set the name and the artifact to spring-boot-aws
5) Change the packaging from jar to war (This does a couple of things that I’ll explain later)
6) Select Actuator and Remote Shell so that we have some RESTful services to test the app with.
7) Click Finish

So, what has this done? It has created a simple Spring Boot application with some REST services like /beans that will return a JSON object of all the beans in your application.

There are two differences between the War variant and the Jar variant. The War variant doesn’t need an embedded tomcat because it will be deployed in a tomcat server so the pom.xml has the spring-boot-starter-tomcat dependency set to “provided”. The Jar variant has the scope tags removed to include this dependency in the jar.



	org.springframework.boot
	spring-boot-starter-tomcat
	provided


The second difference is the ServletInitializer


public class ServletInitializer extends SpringBootServletInitializer {

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(SpringBootAwsApplication.class);
	}

}

Now that you have this application created, we need to generate the war file for deployment onto Amazon AWS. Right click on the pom.xml and select Run As->Maven Install. This will run the build and create the war file in the target folder of your application.

Deploy your application using Amazon Elastic Beanstalk

1) Login to Amazon AWS.
2) In the main control panel select Elastic Beanstalk under Deployment & Management.
3) Click on Create Application in the top right corner.
4) Enter the Application Name and click Next.
5) Environment Tier – Web Server
6) Predefined Configuration – Tomcat
7) Environment Type – Single instance
8) Click Next
9) Select Upload your own, click Browse and locate the war you created earlier.
10) When the application is uploaded you will see the next page where you select your URL.
11) Enter a name and click check availability to see if you can use it.
12) Click Next
13) We don’t need a RDB in this example so click next here.
14) In this next step you are defining the EC2 instance that will be created, if you are using a free trial then stick to the free t1.micro instance type.
15) EC2 Key Pair, can be left unselected. You won’t need it for now and most likely you won’t have one configured yet. This will be covered in a later post.
16) Click Next.
17) In Environment Tags click next again because we don’t care about this.
18) Review the configuration, and then click Launch.

Amazon AWS will now provision your server, install the Tomcat server and deploy the war file that you uploaded. It does take a good 5-10 minutes for this action to complete.

Once it is up and running bizarrely the health starts as red, when you see this you should be able to go to your URL you configured earlier, it will be something like http://your-server-name.elasticbeanstalk.com/beans remember to include the beans part in the URL as that will invoke the Spring Boot REST service to return a JSON object containing all the Spring Beans in your application.

If this doesn’t work it will be worth you going back and checking if your application works locally before you try diagnosing the issue on AWS. However, it should work just fine. Failing that do a git clone https://github.com/marcthomas2013/spring-boot-aws then a mvn package of this project and upload that war and it should work just fine.

To upload another war you will see the upload and deploy button in the middle of the screen.

Screenshot 2015-02-10 22.27.36

Please come back for future posts on hooking up databases to the sample application. I will explain how to build the configuration in AWS as well as how to use Spring Profiles to control the datasource in the application.

Spring Boot NoSuchBeanDefinitionException

At the moment I’m in the process of getting to grips with Spring 4, and Spring Boot. I had created a Service PeopleService class that implements an interface IPeopleService. However, when I added the following code to my main method in my Spring Boot application


@Bean
public CommandLineRunner init(
	final PeopleService peopleService) {

	return new CommandLineRunner() {
		@Override
		public void run(String... strings) throws Exception {
			peopleService.createTestData();
		}
	};
}

I get the following error:


Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [rota.services.impl.PeopleService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

The reason for this is Spring Autowires to the Interface and not the implementation. So, what I needed to do was use the IPeopleService in my init() method instead.