“Hooking” a Spring Java application

I’ve been looking at the implementation / support of the concept of aspect-oriented programming in Spring for quite a while now, unsure to see a meaningful use of it (except for logging and caching, maybe). But maybe viewpoints like this generally grow out of lack of simple, straightforward examples close to ones day-to-day life. So, recently I looked at it again and found something to indeed use it for: Extend a given Spring application using “hooks” and scripting languages.

Overall idea

In our backend we run a legacy document management system which is pretty much customizable / extensible, and a lot of this customizing does happen inside using hooks and the custom scripting language used in this platform. Given the idea of hooks in general, extending a Spring Java (web) application using features like this seems a pretty good playground for using AOP, doesn’t it? So, here we go…

Sample implementation

There is a sample project to be found here. You need maven2 and an up-to-date JDK for it – as the example uses JSR-223 (Scripting for the Java Platform), you will need a Java 6 platform to get goin’.

The project, generally, looks like this:

001-projectstructure

  • There is a service interface (1) and a class implementing this service (2) mocking some “real” business functionality.
  • An implementation of an advice/interceptor (3) takes care of actually running scripting functionality at times.
  • There is some “servlet application infrastructure” (4), like a servlet exposing the business logic to an end user, and a ContextListener setting things up correctly on application startup.
  • A WEB-INF/scripts/ folder (5) contains a sample script used throughout this application.
  • And, of course, there is the usual mess of infrastructure, like a Spring context definition, a deployment descriptor and a welcome-jsp (6).

It is a maven2 project so you start best by downloading the file, extracting things, and start the application using mvn jetty:run. By then, browsing to http://localhost:8080/plugger/VisualizerServlet will leave you with a plain “hello world”, plus a bunch of log messages in your console you used to start the application. So far, so good.

What happens?

  • Generally, the application is a maven2 war artifact configured to run with the maven-jetty-plugin. You might as well build a war file from it and deploy it to your favorite servlet container / app server if you prefer. Along with this, the project provides metadata to open and run it in Eclipse IDE, and NetBeans can work with maven2 projects out of the box as well simply on top of pom.xml.
  • The application contains /WEB-INF/scripts/, which, on startup, gets copied to your temporary folder (System.getProperty("java.io.tmpdir")) from where it is used by the application. This copying is done on application (webapp) context startup by the ScriptInstallerContextListener registered in web.xml.
  • With the webapp context starting up, there’s also a Spring application context started and a servlet (VisualizerServlet) registered with the webapp context. The servlet does access the Spring context using WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext()). In a real-world application, chances are you would eventually use Spring MVC or something like this, but for a test case as easy as this, a plain servlet is perfectly sufficient. Subsequently, the servlet tries to get a bean implementing the IIncredibleService interface from the Spring context and calling one of its methods.
  • Spring context, finally, is configured to proxy the actual implementation of this service and add an interceptor, PluggerAdvice, implementing MethodBeforeAdvice and, so, being invoked whenever any of the methods in the proxied implementation is to be accessed:

001b-config-spring

In the end, looking at the log output after starting the application and accessing the servlet once, you see what happened:

  1. First, there are log statements outlining that the servlet is being called and doing things.
  2. Then, there is the PluggerAdvice getting its work done, and then, finally…
  3. … the business component is being called.

002-log

Adding to this, when the application is running, you should see a folder “scripts” containing a “test.groovy” in your temporary folder, which you might want to edit and play along with to see things happening. Both the code and the logging statements are pretty straightforward, so getting started with this should be rather easy. 😉

So what?

It doesn’t really seem spectacular at first sight. What happened basically is that the application did load a piece of (Groovy) script off the local hard drive and did execute it in course of processing a business method. For my use cases, this is interesting for two reasons:

  • Given the AOP / proxying mechanism used here is generic enough, this is something that can be applied to virtually every component declared and used in a Spring application, other than the servlet filter technology which can do similar things but just wrapping around an HTTP servlet, acting on a HTTP request basis while, making use of the AOP mechanism, every single method call can be intercepted and extended using custom scripting code.
  • Indeed, it is scripting code loaded into the application at runtime off some part of the file system which doesn’t belong to the webapp context, so this seems a rather quick-and-dirty way of extending an application at runtime without having to redeploy.

I will by now spend some time playing with this implementation to extend it, see what else is possible, see where it can get me in “real-life” development. Not really talking about performance issues (checking whether on-disk files were updated by the user at runtime, loading them, …) or even security considerations (as this approach does pretty well compromise most of the Java/EE security concepts), it seems an interesting conceptual architecture, and I am curious to see what can be done with this. 🙂

References