Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Thursday, November 22, 2012

Flexible language

I've been learning Lisp for few years now, and every Lisp book I read keeps saying that Lisp is a flexible language that you can extend to the degree when it fits naturally to your domain. It's easy to say, but what exactly does this phrase mean? After all, when you program in your non-Lisp language, don't you modify it for your domain problem? I've been thinking about it for a long time, and only recently I started to understand what flexibility really means. There is a difference between using the language and changing the language to solve a problem. In this post I will try to show the difference based on a simple example.

Problem

Suppose you have a process that listens to a message queue. The messages are just ordinary maps. If the map contains certain keys, one or more handlers must be invoked. Here is a matrix that shows which handler is invoked for which key

For example, if the map has key a, then DocHandler and AlertHandler need to be called. If it has key b, then NoteHandler and AlertHandler are called. In reality there might be more keys and more handlers, but for simplicity we limit our example to three keys and three handlers.

Java

Let's see how this can be implemented in Java. I chose Java just as an example of non-Lisp language. You can pick any other non-Lisp language instead.

public class SimpleMessageListener {

    public List onMessage(Map message) {
        List result = new LinkedList();
        if (isDoc(message)) {
            result.add(handleDoc(message));
        }
        if (isNote(message)) {
            result.add(handleNote(message));
        }
        if (isAlert(message)) {
            result.add(handleAlert(message));
        }
        return result;
    }

    // Decision makers

    private boolean isDoc(Map message) {
        return message.containsKey("a") || message.containsKey("c");
    }

    private boolean isNote(Map message) {
        return message.containsKey("b") || message.containsKey("c");
    }

    private boolean isAlert(Map message) {
        return message.containsKey("a") || message.containsKey("b");
    }

    // Handlers

    private String handleDoc(Map message) {
        return String.format("Document:%s:%s", message.get("a"), message.get("c"));
    }

    private String handleNote(Map message) {
        return String.format("Note:%s:%s", message.get("b"), message.get("c"));
    }

    private String handleAlert(Map message) {
        return String.format("Alert:%s:%s", message.get("a"), message.get("b"));
    }
}

The internals of handle- methods might be very different in reality. Consider the fact they have the same structure as a coincidence. What is not coincidence though is the structure of is- methods. Those methods are identical indeed.

Is this code clean? I would say, no. The main issue is that it's split in three separate but closely related parts. If tomorrow I introduce another message key and a new handler, I have to change three places in the code. Another problem is the code duplication in two spots: a series of if-statements and a group of is- methods.

The last thing to notice about this code is that it's hard to see what kind of problem it's trying to solve. If I didn't provide a matrix which maps message keys to handlers, it would take even more time to figure out what the code is doing. Can we make this code better?

Let's rewrite it as follows:

public class FunctionalMessageListener {

    private interface Handler {
        void handle(Map message, List acc);
    }

    private class DocHandler implements Handler {
        private boolean isDoc(Map message) {
            return message.containsKey("a") || message.containsKey("c");
        }
        public void handle(Map message, List acc) {
            if (isDoc(message)) acc.add(String.format("Document:%s:%s", message.get("a"), message.get("c")));
        }
    }

    private class NoteHandler implements Handler {
        private boolean isNote(Map message) {
            return message.containsKey("b") || message.containsKey("c");
        }
        public void handle(Map message, List acc) {
            if (isNote(message)) acc.add(String.format("Note:%s:%s", message.get("b"), message.get("c")));
        }
    }

    private class AlertHandler implements Handler {
        private boolean isAlert(Map message) {
            return message.containsKey("a") || message.containsKey("b");
        }
        public void handle(Map message, List acc) {
            if (isAlert(message)) acc.add(String.format("Alert:%s:%s", message.get("a"), message.get("b")));
        }
    }

    private List<Handler> handlers() {
        return Arrays.asList(new DocHandler(), new NoteHandler(), new AlertHandler());
    }

    public List onMessage(Map message) {
        List result = new LinkedList();
        for (Handler handler : handlers()) {
            handler.handle(message, result);
        }
        return result;
    }
}

In this version we eliminated ugly if-series, and group together decision making logic and message handling. From that perspective the code became cleaner, but not necessarily clearer. Now it actually takes more effort to understand what the code is doing. Also, the duplication inside the is- methods is still there. We can fix it by extracting it to some abstract class or utility method. We can also use Java reflection within handlers() method to build a collection of handlers without explicitly specifying them. All these manipulations arguably make the code cleaner, but... one thing we'll never be able to fix is the separation between decision making logic and message handling. Whatever you do, there will always be the if-statement that checks if you need to process the message, and the message processing logic itself. Those two things will always be separate. Here is the point where we hit the language constraints.

Clojure

Now let's try to solve the same problem in Lisp and see if we can fix the language to eliminate the last issue from the paragraph above. Here is the direct translation of the previous Java snippet to Clojure dialect of Lisp

(defn- doc-handler [msg]
  (let [a (msg :a), c (msg :c)]
    (when (or a c)
      (format "Document:%s:%s" a c))))

(defn- note-handler [msg]
  (let [b (msg :b), c (msg :c)]
    (when (or b c)
      (format "Note:%s:%s" b c))))

(defn- alert-handler [msg]
  (let [a (msg :a), b (msg :b)]
    (when (or a b)
      (format "Alert:%s:%s" a b))))

(defn- handlers []
  [doc-handler note-handler alert-handler])

(defn on-message [msg]
  (letfn [(handle [acc h]
            (if-let [res (h msg)]
              (conj acc res)
              acc))]
    (reduce handle [] (handlers))))

This code is already easier to read, but we can do even better. The separation between decision making logic and message handling is still there. At this point we should ask the question: what kind of code do we want to see there? And my answer is: I want to replace the -handler methods above with the following code

(handler doc [a c]
  (format "Document:%s:%s" a c))

(handler note [b c]
  (format "Note:%s:%s" b c))

(handler alert [a b]
  (format "Alert:%s:%s" a b))

You see: no conditionals. Handlers are self-sufficient entities which know when they have to be applied and how. In their signatures they explicitly declare what kind of parameters they expect, and in the body they just use those parameters. No boilerplate, clean and simple. The beauty of Lisp is that you can actually implement that code. The way you do it is by creating a macro which will generate the appropriate functions. Creating a macro is not a simple task, I spent quite some time to get this one working, but it's worth of doing, because now the code is clean and clear.

We can make one additional step further by moving the handler declarations inside the handlers() method. (We need one small macro for that.) And here is the final solution

(defmacro handler [name args & body]
  `(fn [~'msg]
     (let [~@(interleave args (map (fn [x] `(get ~'msg ~(keyword x))) args))]
       (when (or ~@args)
         ~@body))))

(defmacro build-handlers [& body]
  `(defn- handlers []
     [~@body]))

(build-handlers

  (handler doc [a c]
    (format "Document:%s:%s" a c))

  (handler note [b c]
    (format "Note:%s:%s" b c))

  (handler alert [a b]
    (format "Alert:%s:%s" a b)))

(defn on-message [msg]
  (letfn [(handle [acc h]
            (if-let [res (h msg)]
              (conj acc res)
              acc))]
    (reduce handle [] (handlers))))

As I said, the first macro might be cryptic, but look at the highlighted part. This is the essence of our problem, and it cannot be done any simpler. Suppose, we need to implement a new handler which should be called if key c is present in the message. Here what we would need to add to build-handler's body to implement this new requirement

  (handler new [c]
    ...)

Simple, right? And what if a new key is added to the message which should be processed by document handler? Here is what we need to change

  (handler doc [a c d]
    ...)

We just add a new key to the function's parameter list. That's it — one-word change.

Summary

Lisp is the most powerful programming language. By that I mean you can change the language in such a way that the solution to any particular problem can be expressed in the simplest possible way. By changing the language, you can remove all the barriers between the language and the problem domain. I hope I demonstrated this in my simple example.

Resources

Clojure source code for this blog, along with the unit tests.

Sunday, October 31, 2010

SpringOne2GX 2010

Last week I attended SpringOne2GX conference in Chicago, the main event in Spring/Groovy/Grails community. Here I want to post my brief review of this conference.

First impressions

The hotel (Westin Lombard) was nice and clean. Internet: there were 2 wireless networks and one cable - everything was free and worked pretty well, signal was good in almost all rooms. The conference reception was well organized - every participant received bunch of souvenirs and special edition of NFJS magazine. I saw hundreds of smiling and happy people of different ages and different outfits. Most of them with Macs. Most of them know each other. The food was fantastic, especially dinner with wine and beer.

Day 1


The first day was mostly introduction and orientation. There was only one talk on the schedule.

Rod Johnson - Keynote (video)

I thought Spring was initially created 7 years ago but the oldest class in the source tree is dated by January 17, 2001, so Spring is actually almost 10 years old. Because of the anniversary the main theme of the presentation was: Where Spring goes in the next decade.

Since the core framework is well crafted already, the focus will be on the integration and making Spring portfolio as a platform for applications. There are three key values in Spring - portability, productivity and innovation - and the platform will be built along those dimensions.

Portability

In the past SpringSource made a good job by providing a framework that make Java applications easily portable across different application servers. The goal for the next decade is to expand the same portability to the cloud - Google AppEngine, vFabric, vmforce, etc.

Productivity

As we all know the ultimate reason of the Spring existence is to make the life of application developer easier, our work is more productive. The framework hides the low-level boilerplate, and provides well defined abstractions. In the next year there will be several features added to the Spring portfolio. Rod mentioned some of them:
- Seamless GWT integration
- Database reverse engineering with roundtripping support in Spring Roo 1.1. You will be able to generate the domain object tree based on your database schema, and it will be updated every time you change the database.
- Spring Payment Services project with Visa integration.

Another aspect of productivity is a tool suite, and here Spring gives you STS. Rod invited Christian Dupuis on the stage, where he demoed how to developed Grails applications in STS. If you are a Grails developer you should definitely take a look at the latest version of STS - it will increase your productivity significantly.

Innovation

There will be several new projects released in the Spring portfolio soon:
- Spring Social - application abstraction for social networks.
- Spring Mobile - platform for multi-device applications.
- Spring-AMQP - API for integration with RabbitMQ.
- Spring Data - API to work with NoSQL databases, in particular Neo4J support in Spring Roo.

Keith Donald demoed GreenHouse project and corresponding iPhone app. This is a reference implementation of Spring Mobile and Spring Social, and this app was really really useful during the conference when I needed to check the schedule and find the room.

At the end of the presentation Rod introduced, and Mik Kersten demoed, the next big thing - Code2Cloud. It's basically a tool that allows you to keep and manage your entire development environment in the cloud: the running app, the source code, the issue tracker, and the build server. Everything is in the cloud and configured by mouse click. It looks cool, and it definitely will be a buzz word in the next year, but I'm not sure if many people will use it. We'll see.

Day 2


I'm going to write only about technical sessions I attended.

Jürgen Höller - What's new in Spring Framework 3.1? (video)

That was one of the best talks of this conference: technical, right to the point, with well-wrtten slides, and personal charm of the presenter. Despite the number 3.1 in the title, Jürgen actually covered three versions of Spring framework: 3.0, 3.1, and 3.2. I'm going to briefly mention the interesting features, and if you want more details you can check the excellent on-line documentation.

Spring 3.0

- Custom annotations. You can create your own annotation by combining multiple existing annotations in one group. Spring automatically detects your annotation during the application context startup, and no special configuration is required. This is a very handy feature, especially when you copy-paste the same annotation group over and over again.

- Configuration classes and annotated factory methods. If you annotate a method with @Bean annotation Spring framework will make the output of the method a Spring bean. There are some other annotations supported, e.g. @Lazy.

- Standardized annotations. Spring now supports JSR-330 @Inject, JSR-250 @ManagedBean, and EJB 3.x @TransactionAnnotation.

- EL++. Expression language can be used now in bean definitions inside appcontext XML, and also in component annotations. Very powerful feature.

- REST support. Spring provides RestTemplate for client code, @PathVariable annotation, and special view resolvers on the server side. It's very interesting topic - check the documentation for details.

- Declarative model validation. You can specify data constraints right in your code by using annotations - very similar to what you have in GORM.

- Improved scheduling. New namespace, and @Scheduled and @Async annotations makes your appcontext smaller and more readable.

If you follow Spring releases, you probably use some or most of these features already. Now let's see what Spring 3.1 brings to us.

Spring 3.1

- Environment profiles for beans. Similar to Maven profiles but works in runtime. The idea here is to create a single deployment unit for all environments and enable certain Spring beans for specific environment. I can't wait to try this feature in our enterprise project.

- Cache abstraction. After 5 years of hibernation this feature is finally implemented. Spring provides an API to work with distributed cache, in particular in cloud environments. There will be adapters for most popular cache implementations, such as EhCache, GemFire, Coherence.

- Conversation management, or how Jürgen calls it HttpSession++. It's basically an extension of HttpSession shared across multiple browsers and window tabs. Looks very interesting.

- Enhanced Groovy support.

- c: namespace, which is a shortcut for <constructor-arg>, analogous to p: namespace for properties. Small feature that makes your appcontext consistent and more readable.

Spring 3.2

Java SE 7 support, JDBC 4.1, support for fork-join framework, general focus on concurrent programming.

Jeff Brown - GORM inside and out

This talk was also good. I worked a bit with GORM before, and had an idea how it's implemented, but it was useful to hear more details from one of the developers.

Jeff started with the background of GORM, the complexity of Hibernate and JPA, and how GORM solves this problem following convention-over-configuration and sensible defaults strategy. He showed how to model the domain objects, what happens behind the scene when you link objects together, how to specify uni- and bi-directional relationships, and how to change default collection implementation in case of one-to-many relationship.

During the presentation he was switching back and forth between sides and terminal, so it was easy to follow and understand the evolution of the sample application. He explained how to introduce various constraints into the model and how Grails would validate them. One of the interesting features I didn't know about was how to test internationalized error messages. You don't need to change your locale for that, simply add lang=your_language parameter to the URL, and Grails will switch to that language for all subsequent requests. Pretty handy.

He concluded the talk by showing how dynamic finders are implemented in GORM using Groovy metaprogramming feature. Interesting part here is that you can implement similar things in your Groovy code using the same technique, basically having custom mini-GORM in your Groovy project.

Venkat Subramaniam - Improving your Groovy code quality

The title of this presentation was little bit misleading for me. I expected Venkat to show some Groovy specific mistakes and how to avoid them. Instead, he was talking about the errors that in most cases are equally applied to any programming language. He mentioned various code smells and explained how to fix them. If you are interested, you can download the slides from Venkat's web site.

He also gave an advise how to maintain the high code quality:
- Have a respectable colleague review your code.
- Use code analysis tools like CodeNarc and Sonar Groovy plugin.

One of the topics he covered was the usage of the 'return' keyword in Groovy. That was interesting. Compare the following two functions and guess what they return:

def func1() {
try {
5
} finally {
return 22
}
}

def func2() {
try {
5
} finally {
22
}
}

Paul King, Guillaume Laforge - Groovy.DSLs (from: beginner, to: expert) (video)

This would be very nice presentation if the speakers didn't try to cover too much. This talk could be easily split into two: one is an overview of Groovy language and another one is DSL. Unfortunately they spent lot of time on theoretical DSL part and Groovy overview, so the practical DSL part was too short from my perspective. The good thing though is that I have slides now, so I can dig deeper into this subject at my spare time.

In the second part of the talk Paul and Guillaume explained which features of Groovy language make it so simple to create DSLs. Here are some of them:
- Static imports and import aliases.
- Simplified collection syntax.
- Small or no language noise.
- Aggregating multiple method calls using 'with' construct.
- Closures.
- Operator overloading.
- Metaprogramming.

In the last part speakers talked about different patterns and techniques of DSL implementations. They provided a comprehensive list of books you might want to read if you are interested in building DSLs.

Adrian Colyer - Technical keynote (video)

Adrian's talk was mostly a reiteration of Rod's keynote from the previous day with some technical details. He mentioned Spring Payment and Spring Data projects, bean profiles and cache support in the Spring core. He showed Spring portability in action by providing links to Spring applications deployed on Google AppEngine and vmForce.

Another interesting part was 20 minutes dedicated to RabbitMQ and Spring-AMQP. He even mentioned Spring-Erlang project which is supposed to be a convenient abstraction on top of standard Jinterface library.

As a continuation of innovation theme Graeme Rocher demoed GORM support for NoSQL databases. That was cool. He simply uninstalled Hibernate plugin and installed Redis plugin, without touching data model. Everything worked perfect. Right now Spring works with Redis and GemFire, but soon they are going to add support for CouchDB, Cassandra, Riak, Neo4j, and MongoDB. Another interesting thing Graeme showed was grails-console. It's a pretty nice tool, you should check it out. It allows you to interact with the Grails data storage using GORM features. Very handy.

Another co-presenter was Keith Donald who demoed Spring Social and Spring Mobile. He explained how OAuth works, and how interoperability with social networks was implemented in GreenHouse.

The keynote was concluded by Jon Travis who demoed SpringInsight.

Day 3


Venkat Subramaniam - Functional programming in Groovy

That was an excellent talk and nice start of the new conference day. Venkat explained main concepts and values of functional programming, and illustrated the theory with comprehensible examples.

He compared imperative and functional style of programming by showing how to implement for-loop using inject() function in Groovy. I think it was one of the best explanations of functional folding I've ever heard. He also demonstrated map and filter operations using collect() and findAll() methods.

He clarified the difference between function value and closure, and between iterative procedure and iterative process. He gave an example on how to pass closure as a parameter to simulate function object in Groovy. He also showed how to replace tail-recursion, which Groovy doesn't support, with inject() method call.

The presentation was concluded with an example of how to use functional techniques to build DSLs in Groovy.

Matthias Radestock, Mark Pollack, Mark Fisher - RabbitMQ and Spring-AMQP (video)

If you read my blog, you know that RabbitMQ is one of my latest interests. I decided to go to this talk just to see how the creators would present their projects. It turned out to be a nice introduction to RabbitMQ and Spring-AMQP. They explained main concepts of AMQP and how it is different from JMS. Here I want to give you some ideas which were not obvious for me when I started working with RabbitMQ.

- Messaging is all about decoupling, and AMQP is much more flexible than JMS in terms of publisher-consumer decomposition.
- All resources are dynamically created and destroyed by clients - the static pre-configuration is optional.
- Exchanges are stateless, they don't keep messages, they only copy and dispatch them. Queues hold the messages and deliver one message to a single client. They neither do routing nor message copying.
- Queue never receives the same message twice.
- If the message doesn't match routing key it's dropped.
- Because of the open protocol, you can use all available TCP tools to monitor your message traffic.

Besides AMQP implementation RabbitMQ also provides some other useful features like custom exchanges, exchange-to-exchange routing, different protocol adaptors, etc. Spring, as usual, gives you a consistent API on top of the RabbitMQ client which hides all low-level boilerplate and makes your application code more readable.

Good presentation, great guys.

Craig Walls - Developing social-ready web applications (video)

This presentation was about integrating your Java code with different social networks. There are three types of such integration: widgets, embedded code, REST API. Craig briefly explained first two, and then dived into REST.

All popular social networks provide REST API which allows you to communicate with them. For simple operations, like search, you can just use standard Spring RestTemplate class to retrieve the data. Try for example the following URLs:
- http://api.twitter.com/1/friends/ids.xml?screen_name=ndpar
- http://search.twitter.com/search.json?q=s2gx
- https://graph.facebook.com/ndpar

This basic approach fails though if you try to post a new message, because you have to be authorized for update operations. That's where OAuth comes in. The idea behind OAuth is pretty simple: instead of sharing your user-password with different clients, it uses generated tokens. This model is more flexible because if you want to revoke the permission from particular client you don't need to change your password and notify rest of the clients - you just remove that client's token from the list of authorized clients and that's it. The only problem with OAuth and social networks is that they support different versions of OAuth. This problem is solved by Spring Social project.

Spring Social offers consistent template-based API across different social providers. It basically gives you an OAuth aware RestTemplate, so you can do something like this:

TwitterTemplate twitter = new TwitterTemplate(API_KEY, API_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET);
twitter.updateStatus("Hello #s2gx !");
twitter.retweet(26887414177L);

If you are in a social network business, definitely take a look at Spring Social.

Mark Pollack, Chris Richardson - Using Spring with non-relational databases (video)

Relational databases are great, right? They've been with us for ages. Everybody knows how to work with them, how to build SQL statements. Every language provides ODBC library. There are bunch of frameworks that make developer's life easier. So why so sudden buzz around NoSQL?

Mark and Chris started their talk highlighting some problems that exist in relational database world:
- Object-relational impedance mismatch. Complicated mapping of rich domain model to relational schema. Relational schema rigidity.
- Extremely difficult/impossible to scale write operations.
- Suboptimal performance in some cases.

All these issues are addressed in NoSQL databases. Although keep in mind that it's not coming for free - you have to trade off ACID semantics, transactions and some other features of RDBMS. But if scalability is more important for you than consistency then NoSQL is your way to go.

There are tons of NoSQL databases available for you, but they all can be split into 4 categories based on their data model:
- Key-Value: Amazon Dynamo, Redis, Riak, Voldemort.
- Column: Google Bigtable, HBase, Cassandra.
- Document: CouchDB, MongoDB.
- Graph: Neo4j, Sones, InfiniteGraph.

Mark and Chris talked about each type, what their typical use cases are, and how their APIs look like. They showed examples for Redis, Cassandra, MongoDB, CouchDB and Neo4j. Then they introduced Spring Data project which, as everything from SpringSource, simplifies the application development and eliminates low-level code. Right now they support most of the popular NoSQL databases, and they plan to add more in the future.

The project is in active development phase, and the new contributors are welcome. So if it sounds interesting for you, go and check it out.

Day 4


Hans Dockter - Gradle - a better way to build

I never played with Gradle, so I was very curious to see how it looks like. According to Hans, who is the creator of this tool, Gradle is a general purpose build system with Groovy DSL interface. It's written in Java and provides build-in support for Java, Groovy, Scala, web and OSGi projects. It's a build language, so you can extend it for your own purposes if needed.

If you compare it with Ant, Gradle is definitely much better because it's more compact and flexible. It offers dependency resolution with integration with Maven and Ivy repositories. It also has some advanced features like incremental builds for custom tasks and parallel testing.

The only problem I had with this presentation was that Hans kept comparing Gradle with Maven. In my opinion they are not comparable. They have different philosophy if you want. All Maven 'constraints' are imposed by design, so it makes no sense to blame Maven for them. I think Ant-Gradle comparison is more appropriate and that's what Hans should have emphasized.

Other than that the session was pretty informative, and I have a better picture of Gradle now.

Brian Sletten - Groovy + The Semantic Web

I had no idea what Semantic Web was. I saw this term first time on the conference schedule, so I decided to go to this talk just to educate myself. I cannot even briefly describe all the discoveries I made during this presentation because I still feel little bit overwhelmed. I just want to provide some links from Brian's slides that can guide you if you want to learn this concept.

- Semantic Web - article from wikipedia.
- Formal W3C specs: RDF, RDFa, SKOS, SPARQL, OWL.
- SPARQL demo.
- RDFa distiller and parser. Try to feed Brian's test page URL (http://bosatsu.net/nfjs/test.html) to the distiller and see what it returns.
- OG - open graph protocol.
- Jena - Java API to work with Semantic Web.
- Java-RDFa parser.
- Pellet - Java API for OWL.

Conclusion

Whew! This happens to be longer review than I planned initially. If you are still with me you deserve my applause!

There were much more presentations at this conference but because of the tight schedule I had to sacrifice 80% of them. My overall impression from this conference is very positive. If you are a Spring/Groovy/Grails developer I encourage you to go to this event next year. The biggest benefit of it: You start seeing the Spring as a universe, not as a bunch of separate projects. You cannot get this feeling from the documentation, even if it's perfect as the Spring one.

Saturday, January 23, 2010

Distributed cache in Erlang

Implementing distributed cache in Erlang is relatively simple task because concurrency, distribution and failover mechanisms are built in the language. In fact, it's so simple that this task is a part of Erlang tutorial. Here I want to show the complete solution which is only 100 lines of code.

I'm going to implement the cache as a typical Erlang server application, that means set of three modules: server, supervisor and application. As underlined storage I'm using Mnesia database which is a part of standard Erlang distribution. It doesn't probably give you the best performance, but it does provide automatic replication. The cache is deployed on three nodes, each node on a separate machine.



Clients will connect to in-memory slave nodes, the master node is dedicated to persistence.

Configure Erlang cluster


Create file .erlang.cookie containing one line with random text. Copy this file to every machine in a cluster to home directory of the user who will start Erlang VM. Make sure this file has unix permissions 600.

Check /etc/hosts on every box to verify that every machine knows others by name.

Set up Mnesia database


Open terminals on all machines and enter Erlang prompt
ubuntu$ erl -sname master
Erlang R13B01 (erts-5.7.2) [source] [rq:1] [async-threads:0] [kernel-poll:false]
Eshell V5.7.2 (abort with ^G)

macBook$ erl -sname slave1
Erlang R13B02 (erts-5.7.3) [source] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false]
Eshell V5.7.3 (abort with ^G)

iMac$ erl -sname slave2
Erlang R13B03 (erts-5.7.4) [source] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false]
Eshell V5.7.4 (abort with ^G)

From one machine ping other two
(slave1@macBook)1> net_adm:ping(master@ubuntu).
pong
(slave1@macBook)2> net_adm:ping(slave2@iMac).
pong

Create database configuration
(slave1@macBook)3> mnesia:create_schema([slave1@macBook, slave2@iMac, master@ubuntu]).
ok

Start database on all nodes
(master@ubuntu)1> application:start(mnesia).
ok
(slave1@macBook)4> application:start(mnesia).
ok
(slave2@iMac)1> application:start(mnesia).
ok

Create cache table
(slave1@macBook)5> rd(mycache, {key, value}).
mycache
(slave1@macBook)6> mnesia:create_table(mycache, [{attributes, record_info(fields, mycache)},
{disc_only_copies, [master@ubuntu]}, {ram_copies, [slave1@macBook, slave2@iMac]}]).

{atomic,ok}

Stop database and quit Erlang VM
(slave1@macBook)7> application:stop(mnesia).
ok
(slave2@iMac)2> application:stop(mnesia).
ok
(master@ubuntu)2> application:stop(mnesia).
ok

Implement Erlang application


The main module of this application is mycache.erl
-module(mycache).
-export([start/0, stop/0]).
-export([put/2, get/1, remove/1]).
-export([init/1, terminate/2, handle_call/3, handle_cast/2]).
-behaviour(gen_server).
-include("mycache.hrl").

% Start/stop functions

start() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

stop() ->
gen_server:cast(?MODULE, stop).

% Functional interface

put(Key, Value) ->
gen_server:call(?MODULE, {put, Key, Value}).

get(Key) ->
gen_server:call(?MODULE, {get, Key}).

remove(Key) ->
gen_server:call(?MODULE, {remove, Key}).

% Callback functions

init(_) ->
application:start(mnesia),
mnesia:wait_for_tables([mycache], infinity),
{ok, []}.

terminate(_Reason, _State) ->
application:stop(mnesia).

handle_cast(stop, State) ->
{stop, normal, State}.

handle_call({put, Key, Value}, _From, State) ->
Rec = #mycache{key = Key, value = Value},
F = fun() ->
case mnesia:read(mycache, Key) of
[] ->
mnesia:write(Rec),
null;
[#mycache{value = OldValue}] ->
mnesia:write(Rec),
OldValue
end
end,
{atomic, Result} = mnesia:transaction(F),
{reply, Result, State};

handle_call({get, Key}, _From, State) ->
case mnesia:dirty_read({mycache, Key}) of
[#mycache{value = Value}] -> {reply, Value, []};
_ -> {reply, null, State}
end;

handle_call({remove, Key}, _From, State) ->
F = fun() ->
case mnesia:read(mycache, Key) of
[] -> null;
[#mycache{value = Value}] ->
mnesia:delete({mycache, Key}),
Value
end
end,
{atomic, Result} = mnesia:transaction(F),
{reply, Result, State}.

It implements Erlang generic server behaviour and provides three client functions – put, get, remove – with the same signature as similar methods in java.util.Map interface.

Next file is a supervisor for the cache, mycache_sup.erl
-module(mycache_sup).
-export([start/0]).
-export([init/1]).
-behaviour(supervisor).

start() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init(_) ->
MycacheWorker = {mycache, {mycache, start, []}, permanent, 30000, worker, [mycache, mnesia]},
{ok, {{one_for_all, 5, 3600}, [MycacheWorker]}}.

It's going to monitor the main cache process and restart it in case of crash.

Next file, mycache_app.erl, provides methods to start and stop our cache gracefully within Erlang VM
-module(mycache_app).
-export([start/2, stop/1]).
-behaviour(application).

start(_Type, _StartArgs) ->
mycache_sup:start().

stop(_State) ->
ok.

Create application descriptor, mycache.app
{application, mycache,
[{description, "Distributed cache"},
{vsn, "1.0"},
{modules, [mycache, mycache_sup, mycache_app]},
{registered, [mycache, mycache_sup]},
{applications, [kernel, stdlib]},
{env, []},
{mod, {mycache_app, []}}]}.

The last module is optional, it provides a quick way to load our application on VM startup
-module(mycache_boot).
-export([start/0]).

start() ->
application:start(mycache).

That's it. Compile all these modules and copy binaries to all machines in the cluster. Place the binaries in the same folder you created Mnesia configuration.

Run Erlang application


Start Erlang VMs and load the application
ubuntu$ erl -sname master -s mycache_boot
Erlang R13B01 (erts-5.7.2) [source] [rq:1] [async-threads:0] [kernel-poll:false]

macBook$ erl -sname slave1 -s mycache_boot
Erlang R13B02 (erts-5.7.3) [source] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false]

iMac$ erl -sname slave2 -s mycache_boot
Erlang R13B03 (erts-5.7.4) [source] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false]

The cache is ready. You can start using it
(slave1@macBook)1> mycache:put("mykey", "myvalue").
null
(slave2@iMac)1> mycache:get("mykey").
"myvalue"
(master@ubuntu)1> mycache:put("mykey", "newvalue").
"myvalue"
(slave1@macBook)2> mycache:remove("mykey").
"newvalue"
(master@ubuntu)2> mycache:get("mykey").
null

It works! So, what do we actually achieve here with about 100 lines of Erlang code and bit of scripting?

• Distribution I run the app on three physical boxes, and it's transparent for the clients.
• Scaleability To add a new node to the cluster is just a matter of Mnesia re-configuration and copying of binary files to the new box.
• Concurrency Write and remove operations are transactional, and because of concurrent nature of Erlang itself our data is consistent and can be accessed by thousands of client processes.
• Fault tolerance Try to kill mycache process inside Erlang VM; it will be restarted automatically by supervisor and data will be replicated from other nodes to the new process.
• Persistence is optional and provided by Mnesia module.

All these benefits are given for free by Erlang/OTP, and it's not the end.

Call Erlang cache from Java


There are several ways of integrating Erlang applications with other languages. For Java the most convenient one is JInterface library. Here is the implementation of java.util.Map interface that communicates with the cache application we've just developed
import com.ericsson.otp.erlang.*;

public class ErlStringMap implements Map<String, String> {

private final OtpSelf self;
private final OtpPeer other;
private final String cacheModule;

public ErlStringMap(String client, String cookie, String serverNode, String cacheModule) {
try {
self = new OtpSelf(client, cookie);
other = new OtpPeer(serverNode);
this.cacheModule = cacheModule;
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}

public String put(String key, String value) {
return remoteCall("put", key, value);
}

public String get(Object key) {
return remoteCall("get", (String) key);
}

public String remove(Object key) {
return remoteCall("remove", (String) key);
}

private String remoteCall(String method, String... args) {
try {
OtpConnection connection = self.connect(other);
connection.sendRPC(cacheModule, method, stringsToErlangStrings(args));
OtpErlangObject received = connection.receiveRPC();
connection.close();
return parse(received);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}

private OtpErlangObject[] stringsToErlangStrings(String[] strings) {
OtpErlangObject[] result = new OtpErlangObject[strings.length];
for (int i = 0; i < strings.length; i++) result[i] = new OtpErlangString(strings[i]);
return result;
}

private String parse(OtpErlangObject otpObj) {
if (otpObj instanceof OtpErlangAtom) {
OtpErlangAtom atom = (OtpErlangAtom) otpObj;
if (atom.atomValue().equals("null")) return null;
else throw new IllegalArgumentException("Only atom null is supported");

} else if (otpObj instanceof OtpErlangString) {
OtpErlangString str = (OtpErlangString) otpObj;
return str.stringValue();
}
throw new IllegalArgumentException("Unexpected type " + otpObj.getClass().getName());
}

// Other methods are omitted
}

Now from the Java application we can use our distributed cache same way we are using HashMap
String cookie = FileUtils.readFileToString(new File("/Users/andrey/.erlang.cookie"));
Map<String, String> map = new ErlStringMap("client1", cookie, "slave1@macBook", "mycache");
map.put("foo", "bar")

Performance?


Let's deploy Erlang and Java nodes following this topology



Here is the speed of the cache operations I get in the Java client:

write 30.385 ms
read 1.23 ms
delete 21.665 ms

If we remove network, i.e. move all VMs, Java and Erlang, to the same box, we'll get the following performance:

write 2.091 ms
read 1.35 ms
delete 2.057 ms

And if we also disable persistence, the numbers will be

write 1.75 ms
read 1.38 ms
delete 1.75 ms

As you can see, performance is not the best, but keep in mind that the purpose of this post is not to build production ready cache application, but show the power of Erlang/OTP in building distributed fault-tolerant systems. As an exercise, try to implement the same functionality using JDK only.

Resources

• Source code used in the blog.

• Upcoming book where authors seem to implement similar application.

Tuesday, September 22, 2009

Fill parameters in LCDS Assembler methods

Last few days we spent debugging some nasty bug in the code that uses LiveCycle managed collections. We were adding/removing items to/from collections on the server side. We saw that server sent a message to the client, client did receive the message, but then it ignored it and didn't update the collection (data grid). After digging into the client side logs we found the reason of such a misbehaviour.

If you have two destinations that share the same channel

<service id="data-service" class="flex.data.DataService">
<destination id="MyFirstDestination">
<channels>
<channel ref="my-rtmp-channel"/>
</channels>
<properties>
<source>example.MyFirstAssembler</source>
</properties>
</destination>
<destination id="MySecondDestination">
<channels>
<channel ref="my-rtmp-channel"/>
</channels>
<properties>
<source>example.MySecondAssembler</source>
</properties>
</destination>
</service>

LCDS uses fillParameters as a key in the managed collections cache. That means fillParameters must be immutable.

public class MyFirstAssembler extends flex.data.assemblers.AbstractAssembler {

@Override
public int refreshFill(List fillParameters, Object newItem, boolean isCreate, Object oldItem, List changes) {
// Never change fillParameters!
}

@Override
public Collection fill(List fillParameters) {
// Never change fillParameters!
}
}

Adobe documentation says nothing about this, so keep this rule in mind when working with LCDS managed collections.

Resources

• Flex log viewer

Thursday, July 23, 2009

Double in ActionScript, Java, and MS SQL

ActionScript 3

• There are three numeric data types in AS3: int, uint, and Number.
• They are not primitives because they can be instantiated using constructors.
• They are not "real" objects because they cannot be null, and they have default values:

myNumber:Number;
myNumber.toString(); // No NPE thrown

• Default value for type Number is NaN (not zero).

Java

• BlazeDS converts AS3 Number type to Java Double.
• NaN is idempotent of conversion:

NaN (Java) -> NaN (AS3) -> NaN (Java)

• null is not! Keep it in mind when you work with BlazeDS:

null (Java) -> 0 (AS3) -> 0.0 (Java)

If Java NaN doesn't have special meaning in your application, you can use it as a "replacement" for null in Java-Flex communication.

MS SQL

• Doesn't support NaN value for numeric columns.
• All NaN values should be replaced by null before saving entity in the database, otherwise you will get exception:

com.microsoft.sqlserver.jdbc.SQLServerException: The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 24 (""): The supplied value is not a valid instance of data type real. Check the source data for invalid values. An example of an invalid value is data of numeric type with scale greater than precision.

In my current project I'm using all three languages, and I have to convert NaN to null back and forth for every object:

NaN (AS3) <-> NaN (Java) <-> null (Java) <-> null (MS SQL)

So I created small utility class that replaces all JavaBean properties of particular type from one value to another:

ExtendedPropertyUtils.replacePropertyValue(myBean, Double.NaN, null);
ExtendedPropertyUtils.replacePropertyValue(myBean, null, Double.NaN);

Feel free to use it if you have the same problem.

Resources

• Feature request to Adobe to introduce nullable Number type.
• Other solutions for similar issues in BlazeDS.