Showing posts with label RMI. Show all posts
Showing posts with label RMI. 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.