Showing posts with label spring. Show all posts
Showing posts with label spring. Show all posts

Wednesday, December 12, 2007

Zero to RMI with Groovy and Spring

In a recent post, I leveraged Spring to write a simple client-server app using 50 lines of Java.

I've moved it to Groovy. I've included the code below, but here are some fast facts:

  • I used groovyc to compile the Groovy to class files, then used the java command.
  • I could only reduce the code by an import or two. However, I did save 220+ characters (notice how clean the Groovy is). Groovy gurus might be able to improve on this.
  • Note that the Spring config files have more namespaces for Groovy support. Discovering this was the hardest part of the exercise.

Here is the contract between the client and server. This would be in a common package:


interface EasyServer {
Message exchangeMessage(String user)
}

Here is a simple Message class. Of course, for RMI, being Serializable is vital:

import java.io.Serializable

class Message implements Serializable {
private final String message

Message(String message) { this.message = message }

String getMessage() { return message }
}

So far, so good. Now the server implementation:

import org.springframework.context.support.ClassPathXmlApplicationContext

class EasyServerImpl implements EasyServer {

Message exchangeMessage(String user) {
return new Message(user + ", Spring with Groovy rocks!")
}

static void main(String[] args) {
def context =
new ClassPathXmlApplicationContext("server_config.xml")
context.getBean("easyServer")
println("server ready")
}
}

It defines an easy exchangeMessage method and a starting main method. Spring takes care of the rest, as shown in the server_config.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/lang
http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">

<lang:groovy id="easyServer" script-source="classpath:EasyServerImpl.groovy"/>

<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<!-- does not necessarily have to be the same name as the bean to be exported -->
<property name="serviceName" value="easyServerService"/>
<property name="service" ref="easyServer"/>
<property name="serviceInterface" value="EasyServer"/>
<!-- defaults to 1099 -->
<property name="registryPort" value="1199"/>
</bean>

</beans>

For running and compiling the server, simply ensure that these are in the CLASSPATH:
  • SPRING_HOME/dist/spring.jar
  • SPRING_HOME/lib/jakarta-commons/commons-logging.jar

Then it is up and away:

$ java EasyServerImpl

[ Spring magic dust snipped ]

server ready

The client is even simpler:

import org.springframework.context.support.ClassPathXmlApplicationContext

class EasyClient {
private EasyServer easyServer

void setEasyServer(EasyServer easyServer) { this.easyServer = easyServer }

Message exchangeMessage(String user) {
return easyServer.exchangeMessage(user)
}

static void main(String[] args) {
def context =
new ClassPathXmlApplicationContext("client_config.xml")

def easyClient = context.getBean("easyClient")

String output =
easyClient.exchangeMessage("CtJ Reader").getMessage()
println( output )
}
}

The client-side configuration XML:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/lang
http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">

<lang:groovy id="easyClient" script-source="classpath:EasyClient.groovy">
<lang:property name="easyServer" ref="easyServer"/>
</lang:groovy>

<bean id="easyServer" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl" value="rmi://localhost:1199/easyServerService"/>
<property name="serviceInterface" value="EasyServer"/>
</bean>

</beans>

Using the same CLASSPATH, but with a separate window, we establish contact like so:

$ java EasyClient

[ Spring noise snipped ]

CtJ Reader, Spring with Groovy rocks!


Cool stuff...

Monday, December 10, 2007

Zero to RMI in Minutes (or I *heart* Spring)

Recently, I paired with a colleague and sought to experiment with detached objects in Hibernate across a remote connection. Armed with some POJOs and Hibernate files from a project source tree, we decided to write a toy client and server from scratch.

(Admittedly, we had experience with Spring IOC: this is not a tutorial.)

We were fully experimenting with the sandbox in 75 minutes. This may not seem fast but it was astonishing: some days, it seems I can't empty my Recycle Bin in 75 minutes.

Partly for my own reference, but also for CtJ readers, here is a recap of the RMI portion. No Hibernate here. And an extremely "toy" project structure (e.g. no Ant, no packages). But the key idea is there, and it will showcase the power of Spring.

We used Spring 2.5. To be honest, I don't know if this is in Version 1.x. It may well be.

Here is the contract between the client and server. This would be in a common package:


interface EasyServer {
Message exchangeMessage(String user);
}

Here is a simple Message class. Of course, for RMI, being Serializable is vital:


import java.io.Serializable;

public class Message implements Serializable {
private final String message;

public Message(String message) { this.message = message; }

public String getMessage() { return message; }
}

So far, so good. Now the server implementation:


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class EasyServerImpl implements EasyServer {

public Message exchangeMessage(String user) {
return new Message(user + ", Spring rocks!");
}

public static void main(String[] args) {

ApplicationContext context = new
ClassPathXmlApplicationContext("server_config.xml");

context.getBean("easyServer");
System.out.println("server ready");
}
}

It defines an easy exchangeMessage method and a starting main method. Spring takes care of the rest, as shown in the server_config.xml file:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<!-- EasyServerImpl is the implementation -->

<bean id="easyServer" class="EasyServerImpl">
</bean>

<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<!-- register the impl as the 'easyServerService' -->
<property name="serviceName" value="easyServerService"/>
<property name="service" ref="easyServer"/>
<property name="serviceInterface" value="EasyServer"/>
<!-- defaults to 1099 -->
<property name="registryPort" value="1199"/>
</bean>

</beans>

For running and compiling the server, simply ensure that these are in the CLASSPATH:
  • SPRING_HOME/dist/spring.jar
  • SPRING_HOME/lib/jakarta-commons/commons-logging.jar

Then it is up and away:


$ java EasyServerImpl

[ Spring magic dust snipped ]

server ready

The client is even simpler:


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class EasyClient {
private EasyServer easyServer;

public void setEasyServer(EasyServer easyServer) {
this.easyServer = easyServer;
}

public Message exchangeMessage(String user) {
return easyServer.exchangeMessage(user);
}

public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("client_config.xml");

EasyClient easyClient =
(EasyClient) context.getBean("easyClient");

String output =
easyClient.exchangeMessage("CtJ Reader").getMessage();

System.out.println( output );
}
}

The client-side configuration XML:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="easyClient" class="EasyClient">
<property name="easyServer" ref="easyServer"/>
</bean>

<bean id="easyServer"
class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl"
value="rmi://localhost:1199/easyServerService"/>
<property name="serviceInterface" value="EasyServer"/>
</bean>

</beans>

Using the same CLASSPATH, but with a separate window, we establish contact like so:


$ java EasyClient

[ Spring noise snipped ]

CtJ Reader, Spring rocks!

This trivial example uses approximately 50 lines of Java, with no knowledge of RMI. Amazing. It an easy exercise for the reader to package it up into a more formal project, using Ant etc. It is more challenging to add Hibernate, but still tractable: the basic wiring for a client-server playground is right here.

I'm curious as to how many lines of code this would take in Groovy. I think 40 would be easy to attain.

That's cool. I *heart* Spring!

ps. I lifted "*heart*" from Eric.

Tuesday, June 5, 2007

Out of the Ether: Dependency Injection

I enjoy Frank Kelly's blog. He has a recent post about Dependency Injection. I had been sitting on a thought for awhile with respect to DI, and thought I'd write it out here instead of in a comment window.

(Editor's note: be sure to see the post script for strong criticism against the example and the post.)


Here's how I think of DI. We all know this is bad:


class MyClass {
private HashMap myMap = new HashMap();
}

because we should program to the interface, yes? Like so:

class MyClass {
// more flexible, in theory
private Map myMap = new HashMap();
}
The conventional wisdom was always:
Program to the interface, since it will be easy to change the implementation.
True that. But then the implicit kicker was:
And when you are done with your application and have been given a couple of weeks by management to tweak performance while wearing your PJ's and drinking a fine Chardonnay, you can go through the code and select the implementation that's right for you.
Whoa, Nelly! *throws RealityCheckException*. When would that be, again?

What if we tried this:

class MyClass {
private Map myMap;

public MyClass( ... , Map myMap ) {
this.myMap = myMap;
}
}

The beauty of this, IMHO, is truly profound; it took me a long time to appreciate it. Clearly, the class just uses the interface and there is less coupling, more flexibility, etc. But there is more:

The Ether

The power of DI is twofold:

(1) a given class is simplified and works with a minimal contract (the interface)

(2) there are tremendous tools available, outside the class, that allow us to really, truly make different implementations available, and swap 'em out easily. Essentially, these tools control the creation of the MyClass object, and allow us to dictate the given implementation for an interface (e.g. Map). Where are these implementations? Where do they come from? The answer is, from the view of the class: we don't know, they come out of the ether.

And that ether is powerful stuff: DI tools allow us to create mock implementations, single-threaded implementations, file-based implementations, DB implementations, carrier-pigeon implementations -- you name it. The configurations (or annotations) make it truly possible, unlike the hypothetical "free 2 weeks of tweaking paradise" mentioned earlier. You could potentially run several entirely different testing suites, simply by swapping out the ether.

To press the point even further, take the Map interface used above and now multiply this idea by any and all interfaces in your application. That ether is strong mojo.

Whether you use Guice or Spring: tap into this ether and re-discover the power of interfaces. If you're like me, you may discover they are cooler than we ever realized.

Final Score: Dependency Injection 1 Management 0

Post Script

Eric and others have made valid criticisms. See the article/comments, but here is a summary:
  • I blew it on the phrase "any and all interfaces". The coolness of DI doesn't give one license to inject everything. A better version is: consider all of the interfaces in your project and then apply DI judiciously.
  • My example was poor. I wanted to concentrate on the ether, and didn't have the energy to construct a "real-world" example so I went with Ye Olde Collections. I think it is an ok example to illustrate the ether, as long as one understands the criticisms against it. In fact, it might even be a better example because of the criticisms (though this was not by intent: mea culpa).
  • I have used Spring's XML configuration, and have wondered about losing encapsulation as a danger of DI. I haven't yet tried Guice or any of Spring's newer stuff, so I don't know how that might mitigate the issue.