Friday I attended the last day of the Spring Workshop. An interesting menu for today’s dinner: Aspect Oriented Programming, JMX support and Remoting.
Friday, the last day of the Spring Workshop by core committers Alef Arendsen en Rod Johnson. Another dive into the depths of Spring, markably the Spring AOP (Aspect Oriented Programming) module, the Remoting services and the JMX support. Another round of discussions on training developers, best architectural practices, picking and combining technology components etc.
For some – including myself – this was the first (real) encounter with AOP, the concept of defining and applying behavior to a class, external to that class. When we request an object from the Spring IoC Container, we could have set it up in such a way that we receive what we think is the object – or rather some simple implementation of an interface that we program against, but in fact we get an object that has been enhanced with all kinds of additional behavior. It became clear – from what Rod and Alef said but also from the sheer logic of the discussion – that Aspects will play an increasingly important role in Java Design and Development in the years to come.
AspectJ5, soon to be released, will allow use of Java 5 Annotations to specify pointcuts. This allows you to include metadata referring to Aspects in your class code, supported by IDEs, with code completion to select Aspects to have applied to the class or the method. The AspectJ team have allowed the Spring team to make use of their API for these annotations; that means that they have produced a separate jar file that can be used with Spring Framework as well, driving Spring AOP using the same Java 5 annotations. Of course this likely to promote use of those annotations.
Aspects typically are used for infrastructural behavior – and not business functionality – such as logging, security, auditing, transaction management, profiling and performance monitoring. This allows us to keep such functionality out of the domain objects. It also prevents us from having to either have all domain objects inherit from some sort of super class that implements all of that functionality or writing code for this behavior over and over again.
Aspects are applied to joinpoints (method invocations) through pointcuts; these are typically declaratively defined in the ApplicationContext.xml. A pointcut defines how an Advisor is applied to joinpoints, the method-invocation triggers/wrappers.
There are indications that IBM is making heavy use of AspectJ in developing the core of WebSphere; clearly a sign that they at least see a lot of value in the concept, and not just from a theoretical point of view! The discussions among the workshop participants were also quite positive: once you get your head wrapped round the concept of Aspects and the use of one or even multiple orthogonal dimensions to provide behavior to your classes, it becomes very enticing. Some developers can focus on the primary (business) functionality of classes, while others, almost independently, can enhance these classes with generic, infrastructural behavior, that can add transactional control, profiling, auditing etc. to the domain objects. This clearly leads to a whole new way of designing the objects as well as organizing the development effort. Fortunately, again we can start small, adding just a single, low level aspect to see how it goes, gradually increasing the amount of functionality we trust to have outside our classes. Initially it can be a quite scary thought to see your objects doing things that were never programmed into them!
In the lab, we used Spring AOP to add simple profiling behavior to a class: the class registered the number of calls to all or certain specific methods and recorded the time required for these invocations. In no time at all, we had a logfile telling us for test-cases using our Aspected class how many calls were made to the methods (more than I realized) and what time they took.
The profiling behavior was added in a very simple way, declaratively, in the applicationContext.xml that drives the IoC container we used in our application. We already used the IoC Container to get hold of a Service Object – an implementation of the Service Interface that our application was programmed against. This basically means that in this configuration file, that is loaded when the IoC Container is initialized, we have specified a bean gameRenter that is to be instantiated using the specified class DefaultGameRenter.
<bean id="gameRenter" class="com.springframework.training.gamecast.service.DefaultGameRenter" init-method="init" autowire="byType"/>
The application just asks the container for a bean called gameRenter and it expects to get hold of a GameRenter (the name of the Interface). The application’s operations are then using the GameRenter bean returned by the container. We now would like to apply the monitoring functionality to the GameRenter bean our application uses. Instead of changing the code for the GameRenter implementation – code that we may not even have access to – we can use Spring AOP. In the applicationContext.xml configuration file we tell the Spring IoC Container that whenever we ask for the gameRenter bean, it should return a bean that is based on class com.springframework.training.gamecast.service.DefaultGameRenter, but has been enriched with two, orthogonal pieces of functionality. One is TransactionManagement, provided by a bean called transactionManager (configured elsewehere in the configuration file) and the other is Call-profiling, implemented in a bean called callMonitoringInterceptor (instantiated from class com.springframework.training.gamecast.monitoring.CallMonitoringInterceptor). We have added a preInterceptors element with the callMonitoringInterceptor bean to the gameRenter bean definition.
<bean id="callMonitorInterceptor" class="com.springframework.training.gamecast.monitoring.CallMonitoringInterceptor"/> <bean id="gameRenter" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref local="transactionManager"/> </property> <property name="target"> <bean class="com.springframework.training.gamecast.service.DefaultGameRenter" init-method="init" autowire="byType"/> </property> <property name="transactionAttributes"> <props> <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="rent*">PROPAGATION_REQUIRED</prop> </props> </property> <property name="preInterceptors"> <ref bean="callMonitorInterceptor"/> </property> </bean>
Spring AOP relies on dynamic proxying. At runtime, when we ask the container to hand us a GameRenter called gameRenter, the container in fact uses a ProxyFactory to have returned to us a Proxy object that implements the GameRenter interface – because that is essentially all we are asking for – but that wraps our initial DefaultGameRenter object in two additional layers of Advisors, adding Transaction Management and Call Profiling. All our application’s calls to GameRenter are intercepted by these two Advisors, before they reach the DefaultGameRenter methods – if these are reached at all!
What should be very clear: we did not change a single line of code in either the DefaultGameRenter class nor the application using the GameRenter interface. The only thing visible in the application is that it uses the Spring IoC Container to get hold of the GameRenter instance, rather than doing a new DefaultGameRenter() itself. All wiring is in the IoC Container’s configuration file, where we have specified that a request for a GameRenter should result in a DefaultGameRenter enveloped in Transaction management and Call profiling. Removing the profiling from our application is of course as easy as removing the preInterceptor element from the configuration file.
A further level of AOP involves the Introductions. An Introduction allows us to specify new behavior for objects, at runtime. In Spring AOP this means making objects implement additional interfaces. So by applying an IntroductionAdvisor to a bean, the bean will be wrapped by the Spring IoC Container in a proxy object that implements an interface. The implementation of the interface is typically done in a subclass of the DelegatingIntroductionInterceptor, that implements the interface. Note that Introductions usually add state to an object. There will be one Introduction instance per Advised object.
One cool thing about these introductions is that they can even add interfaces to objects that we do not have the Java Code for. If we have a .class file in a jar, we can have our Spring IoC container instantiate instances for us and also have it proxy these instances, adding all kinds of behavior. Even adding additional interface implementations. Note: if you add an Introduction to an object that implements an interface that the object itself already implements, then the Introduction will override the base classes implementation! This can add really weird behavior to your system.
Note that the Spring AOP implementation uses Dynamic Proxies – supported as of Java 1.3. It does not require special compilers – as AspectJ does. Also note that Spring AOP in Spring 1.3 will support a substantial portion of the AspectJ XML expression language for specifying how and where to apply aspects; I supposed this link gives some introductions there.
The second topic of the day was the support Spring provides for exposing beans as MBeans to the JMX Server. I have not worked with JMX before; although I had heard about MBeans and the fact that they support administrative activities, I had never really understood what exactly could be their benefit. See Monitoring Local and Remote Applications Using JMX 1.2 and JConsole on OnJava.com for a useful introduction.
In a very straightforward presentation, Alef demonstrated how Spring can expose any bean it provides to the application from the ApplicationContext as MBean to the JMX Server. Absolutely no invasive coding in the beans themselves is required. Using JDK 5.0 JConsole as JMX Server, with a simple Spring application runnig in IntelliJ where the beans configured in the applicationContext.xml were wired up to be exposed as MBeans as well, it was extremely easy to monitor all Bean instances, inspecting their values and even changing their values on the fly! Read this carefully: While the application is running, you can inspect any bean instance, and you can even invoke operations on
the objects. This allows for interesting interference with running applications, especially in the area of changing configuration details.
Vice versa , Spring allows the definition of beans that are actually proxies for MBeans exposed in the local or even in remote JVMs. That means that your application can be injected with beans set up by Spring based on MBeans (Spring is like an MBean Client here). You define simple interfaces that mimick the attributes and operations exposed by the MBeans. Spring will then create proxy objects for the MBeans that implement these interfaces. In your application you just code against the interfaces. You may regard these MBean-proxy objects as normal Java Objects, instances of your interfaces.
By the way: the Spring applicationContext itself is not currently exposed as an MBean. That is to change shortly. When that happens, you can use JConsole or any JMX tool to inspect (and possibly influence) the current configuration details for the application context.
In a way, we had come full circle when just before 4PM Alef embarked on the last trip in this workshop: Remoting Services in Spring. Spring originated from frustrations with EJB. The one thing that EJB has some value for is the option to publish services to remote clients. However, always bear in mind Fowler’s first law of distributed objects:”Do NOT distribute your objects!”. Anyway, having to work with (possibly) remote services is not always trivial. And it is also something that you would rather not have invade your application code. First of all, infrastructural code should preferably not be written by developers who should focus on business oriented functionality, second of all, if you would change the implementation of the service, switching from remote to local or vice versa of changing the protocol over which to communicatie with the remote service – for example from Http/WebService to RMI – you do not want to change the core of the application.
Spring Remoting does two things: it provides Exporters to implement End Points for Services that need to be published to remote clients. Spring intercepts such remote-originated calls and translates them into local calls to the Service; it handles the results, sending them back to the remote client. This is transparent to the Service itself: you can implement a business service that is published to remote consumers without being aware of that fact in the code. Configuring one or more End Points for such a service is very simple.
The second role of Spring Remoting, perhaps even more valuable, is the Client side ProxyFactoryBean. Simply said: you inject into the client a Business Service, the client is coded against the Business Service interface, and the object that is injected is a Spring proxy that knows how to intercept calls to the methods on the Business Service and translate them into calls to the remote service. In the Client Side application context, you register the Service using one of the Remote ProxyFactoryBeans – their are implementations for Burlap, Hessian, RMI, EJB, Spring HttpInvoker, AXIS and XFire. You configure the ProxyFactoryBean with remote server details (port, server) and you tell it which Service Interface to implement.
In the lab, we implemented a simple GameRenter Service, deploying it as a (Remote) HttpInvokerService in Tomcat. The Service Interface contains the getTitles() method that returns the Titles of all games we have in our catalogue. We then run locally an application that gets the GameRenter injected and invokes the getTitles() method on it. The GameRenter instance that is injected is actually a proxy; all method invocations on this instance are intercepted by Spring and translated into calls to the remote service. It took us no time at all to have our application display a list of game titles that were retrieved from the remote service. Our application code simply had no clue that the GameRenter object it used was not the object guy next door but an object running in a different JVM, possibly continents away.
Switvhing from a remote to a local service – or a different remote service – is matter of configuration details. It is even quite conceivable to configure several RemoteProxyFactoryBeans for various protocols and EndPoints and switch between them at runtime.
All good things..
Around 17.45, after running the final lab, the workshop came to an end. After four days of intensive lectures, demonstrations and labs as well as on- and off-topic discussions, brainstorms and experience-exchanges, we said our goodbyes. We shared a common understanding that our group might be one of the driving forces behind Spring use in The Netherlands. We had had an opportunity to meet the top dogs behind it, learn firsthand about the background and implementation details of the Spring framework and helped shape the workshop for scores of students to come. It has been a pleasant and very satisfying week. I’d like to thank Rod, Alef en Leonard once more for the energy they poured into this week and the candor in sharing their ideas with us. I was pleasantly surprised by their willingness to tell it all, their receptiveness to new ideas and criticism and their patience with us when it just went over our heads.
If this show is coming to a theater near you: do not miss it!
Miscellaneous: Another plug by Alef (on Wednesday) was for SiteMesh – a framework for applying generic layout concepts to the HTML rendered by our View Components. SiteMesh follows the Decorator Design Pattern, one of the reasons Alef prefers it over for example Tiles. Another plug: the Resin servlet container (from Caucho) – extremely fast (though Tomcat 5.5 has largely catched up), very easy confguration, free development and a lousy $500 per server license.
Another hint, this one from Leonard, was to never use the standard Sun JVM in real production systems. JRocket, the BEA JVM, provides an out of the box improvement in performance in the order of 60%. I felt somewhat stupid for never having seriously considered alternative JVMs, even though I had heard about JRocket before. I have not got a clue what licensing issues are involved though.
Alef could not resist plugging another open source endeavour, in which one of his colleagues is involved, called XFire (pronounced at least some of the time as CrossFire); the homepage for XFire is at http://xfire.codehaus.org/. XFire is “a next-generation java SOAP framework. At its core is a light message processing model used to interact with SOAP messages via STAX”. XFire is in a way an alternative to AXIS for SOAP based message transport. XFire is easy to set up, provides a lot of deafulting, is not based on JAX-RPC, has no Client Library yet (will soon be released). XFire aspires to beIntuitive, easy to use API., Be Fast, Allow many different binding methods (traditional java types, OGNL, Castor, JaxME, etc), Create a processing model where your web service model and your java model can develop independently. XFire is currently 0.9 and will see a 1.0 release Summer 2005.