Here I present an example distributed Counter service, which uses modern technology stack consisting of Hazelcast, Jersey 2 and Guice.
The sample is based on an excellent example posted by Piersy and can be downloaded from here:
http://github.com/rafalrusin/jersey2-guice-example-with-test
@Singleton
public class CounterService {
return (Integer) map.executeOnKey("counterKey", new CounterEntryProcessor(delta));
}
}
public class CounterEntryProcessor implements EntryProcessor<String, Integer>, EntryBackupProcessor<String, Integer> {
private final int delta;
public CounterEntryProcessor(int delta) {
this.delta = delta;
}
@Override
public Integer process(Map.Entry<String, Integer> entry) {
int newValue = entry.getValue() + delta;
entry.setValue(newValue);
return newValue;
}
}
The service is just a Guice Singleton. It has an operation called 'increase', which takes a delta and creates EntryProcessor job for Hazelcast to submit to a node, which owns the value at the time and will update it atomically.
Hazelcast is a library, which implements Java Collections in distributed fashion. It handles replication, cluster membership and distributed locking.
Web service is a simple JAXRS REST Resource. Following code invokes Guice service layer from Resouce implementation:
@RequestScoped
@Path("counter")
public class CounterResource {
private CounterService service;
@Inject
public CounterResource(CounterService service) {
this.service = service;
}
@POST
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
public String increase(String delta) {
return "" + service.increase(Integer.parseInt(delta));
}
}
We also have a test case, which invokes the whole stack and does a request to the service. The whole stack is very lightweight. In runs on Jersey with Embedded Tomcat. The test case completes within a few seconds.
In order to run the example, you need to build distribution first (run dist.sh), then start nodes in separate shells and invoke curl POST requests to manipulate counter state. Here's a sample interaction:
./run.sh 8090
./run.sh 8092
...
Jul 05, 2014 10:20:06 AM com.hazelcast.cluster.ClusterService
INFO: [10.0.0.12]:5702 [dev] [3.2.3]
Members [2] {
Member [10.0.0.12]:5701
Member [10.0.0.12]:5702 this
}
Jul 05, 2014 10:20:08 AM com.hazelcast.core.LifecycleService
INFO: [10.0.0.12]:5702 [dev] [3.2.3] Address[10.0.0.12]:5702 is STARTED
...
$ curl -d '1' -H 'Content-Type: text/plain;' http://localhost:8092/webapp/api/counter
22
$ curl -d '1' -H 'Content-Type: text/plain;' http://localhost:8090/webapp/api/counter
23
$ curl -d '5' -H 'Content-Type: text/plain;' http://localhost:8090/webapp/api/counter
28
I think that Hazelcast is a very good step forward into distributed computing. For an every day programmer, it's a set of primitives to manipulate in order to implement a distributed system. It is very easy to integrate it into existing project, which could be either JavaEE or standalone app.