Monday, August 02, 2010

Working with RabbitMQ in Spring applications

Recently SpringSource released Spring AMQP 1.0.0.M1. Now, if you are a Spring shop working with RabbitMQ, you don't need to write low level code to connect to RabbitMQ server anymore. Instead, you can use well-known Spring abstractions (message templates and containers) to produce/consume AMQP messages, the same approach you would use for JMS. Here is my previous example re-implemented using Spring AMQP.

Very simple application classes (sender and receiver)

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;

public class MessageSender {

@Autowired
private AmqpTemplate template;

public void send(String text) {
template.convertAndSend(text);
}
}

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class MessageHandler implements MessageListener {

@Override
public void onMessage(Message message) {
System.out.println("Received message: " + message);
}
}

and pretty standard application context

<context:annotation-config />

<bean id="rabbitConnectionFactory" class="org.springframework.amqp.rabbit.connection.SingleConnectionFactory"
p:username="guest" p:password="guest" p:virtualHost="/" p:port="5672">
<constructor-arg value="lab.ndpar.com" />
</bean>

<bean id="rabbitTemplate" class="org.springframework.amqp.rabbit.core.RabbitTemplate"
p:connectionFactory-ref="rabbitConnectionFactory"
p:routingKey="myRoutingKey"
p:exchange="myExchange" />

<bean id="messageSender" class="com.ndpar.spring.rabbitmq.MessageSender" />


<bean class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer"
p:connectionFactory-ref="rabbitConnectionFactory"
p:queueName="myQueue"
p:messageListener-ref="messageListener" />

<bean id="messageListener" class="com.ndpar.spring.rabbitmq.MessageHandler" />

That's it, simple and clean.

Resources

• Spring AMQP official page

• Source code for this blog

Wednesday, March 31, 2010

Integrating RabbitMQ with ejabberd

Last few days I've been trying to make RabbitMQ and ejabberd work smoothly together by means of mod_rabbitmq gateway. The official mod_rabbitmq document is pretty clear but the installation chapter is rather short. Plus, it presumes that ejabberd is installed from the source tree, which might not be the case. Here I want to give you more detailed instructions on the installation/configuration process in case mod_rabbitmq doesn't work for you out of the box.

My environment is Ubuntu 9.10 with rabbitmq-server and ejabberd packages installed via apt-get. Both RabbitMQ and ejabberd are up and running. Now I want them to talk to each other and route messages properly.

Compiling mod_rabbitmq


If you have the same environment as mine you can just download the binary and the header files, and copy them to the corresponging ejabberd folders (see last two lines in the bash snippet below). Alternatively you can compile mod_rabbitmq.beam file yourself:

$ git clone git://git.process-one.net/ejabberd/mainline.git ejabberd
$ cd ejabberd
$ git checkout -b 2.1.x origin/2.1.x
$ cd src
$ wget http://hg.rabbitmq.com/rabbitmq-xmpp/raw-file/73c129561101/src/mod_rabbitmq.erl
$ wget http://hg.rabbitmq.com/rabbitmq-xmpp/raw-file/73c129561101/src/rabbit.hrl
$ ./configure --disable-tls
$ make
$ sudo cp mod_rabbitmq.beam /usr/lib/ejabberd/ebin/
$ sudo cp rabbit.hrl /usr/lib/ejabberd/include/

Configuring mod_rabbitmq


You need to know the short name of the machine you are running RabbitMQ on. Use hostname -s command for this. Open /etc/ejabberd/ejabberd.cfg file for edit, find modules section, and add mod_rabbitmq stanza to the list

{modules,
[
{mod_adhoc, []},
...
{mod_rabbitmq, [{rabbitmq_node, rabbit@yourhostname}]},
...
{mod_version, []}
]}.

Replace yourhostname with your machine short name. In my case it was ubuntu.

Setting up cookie


To make RabbitMQ and ejabberd work together, they have to run in the same Erlang cluster. That means they have to use the same cookie file. By default RabbitMQ is installed under rabbitmq user with /var/lib/rabbitmq home directory, and ejabberd under ejabberd user with /var/lib/ejabberd home directory. If you compare their cookies

$ sudo cat /var/lib/rabbitmq/.erlang.cookie
$ sudo cat /var/lib/ejabberd/.erlang.cookie


they will most likely be different. That's why if you restarted ejabberd now you would see exception in RabbitMQ log: "Connection attempt from disallowed node ejabberd@ubuntu". To fix it just copy one cookie file to another

$ sudo /etc/init.d/ejabberd stop
$ sudo mv /var/lib/ejabberd/.erlang.cookie /var/lib/ejabberd/.erlang.cookie.orig
$ sudo cp /var/lib/rabbitmq/.erlang.cookie /var/lib/ejabberd/.erlang.cookie
$ sudo chown ejabberd:ejabberd /var/lib/ejabberd/.erlang.cookie
$ sudo /etc/init.d/ejabberd start

The installation part is now done, and you are good to go.

Adding rabbit buddy to your roster


The rabbit's JID comprises two parts: exchange name and routing domain. To find the latter one, look at the /var/log/ejabberd/ejabberd.log file. Searching for "Routing" you should get something like this

=INFO REPORT==== 2010-03-30 21:35:22 ===
{contacted_rabbitmq,rabbit@ubuntu}

=INFO REPORT==== 2010-03-30 21:35:22 ===
I(<0.314.0>:mod_rabbitmq:90) : Routing: "rabbitmq.jabber.ndpar.com"


This is the buddy's domain. For the name you can use any exchange name available in the RabbitMQ server. Run sudo rabbitmqctl list_exchanges command and pick up the name from the list. I use amq.fanout exchange which exists in every RabbitMQ server. So I go to my IM client (Adium) and add this user to the buddies list

amq.fanout@rabbitmq.jabber.ndpar.com

Rabbit's greetings


To publish a message to RabbitMQ I use the same Groovy script as in the previous post. I just amended the exchange name and routing key

channel.basicPublish 'amq.fanout', '', null, 'Hello, world!'.bytes

Run the script and voilà, you've got mail



Troubleshooting


Here are some hints for you if something goes wrong.

• While working with mod_rabbitmq keep an eye on the log files of both RabbitMQ and ejabberd:

$ tail -f /var/log/ejabberd/ejabberd.log
$ tail -f /var/log/rabbitmq/rabbit.log

• Check which exchanges, queues and bindings the RabbitMQ server has:

$ sudo rabbitmqctl list_exchanges
$ sudo rabbitmqctl list_queues
$ sudo rabbitmqctl list_bindings

• If you screw up something there, you can roll back to the default values:

$ sudo rabbitmqctl stop_app
$ sudo rabbitmqctl reset
$ sudo rabbitmqctl start_app

• Check ejabberd web admin, it has lots of information there

http://yourdomainname:5280/admin

• If your IM client is Adium, check its folder periodically — it tends to collect some garbage there:

~/Library/Application Support/Adium 2.0/Users/Default/libpurple

Resources

• Tony Garnock-Jones' presentation slides about RabbitMQ and its extensions

Sunday, March 14, 2010

Get started with RabbitMQ

RabbitMQ is an open-source implementation of AMQP. If you don't know what AMQP is, I encourage you to check it out on the official web site, or alternatively read articles listed on the reference page. Here I want to mention only the reasons why it drew my attention as an Erlang enthusiast and Java developer working in financial industry:
  • AMQP is a replacement for TIBCO Randezvous;
  • in terms of functionality it's a superset of JMS;
  • it's written in Erlang, which means fault-tolerance, reliability and high performance.

In this blog post I just want to show how to install RabbitMQ on Ubuntu box, and verify that it works with simple Groovy client.

Installing RabbitMQ server


As everything with Ubuntu, this step is pretty trivial:

$ sudo apt-get install rabbitmq-server

The only requirement for this package is Erlang distribution. If you already have Erlang installed on your system, the installation of rabbitmq-server is a quick procedure. The following directories will be created during the installation:


/usr/lib/rabbitmq/binexecutables added to the path
/usr/lib/erlang/lib/rabbitmq_server-1.x.xcompiled modules
/var/lib/rabbitmq/mnesiapersistent storage for messages
/var/log/rabbitmqlog files (e.g. startup_log, rabbit.log)

After installation is finished the RabbitMQ server is started and listens to incoming requests on port 5672. You can check /var/log/rabbitmq/startup_log file to see if everything was ok.

Groovy clients


I followed official Java client API to build two scripts: consumer.groovy
import com.rabbitmq.client.*

@Grab(group='com.rabbitmq', module='amqp-client', version='1.7.2')
params = new ConnectionParameters(
username: 'guest',
password: 'guest',
virtualHost: '/',
requestedHeartbeat: 0
)
factory = new ConnectionFactory(params)
conn = factory.newConnection('lab.ndpar.com', 5672)
channel = conn.createChannel()

exchangeName = 'myExchange'; queueName = 'myQueue'

channel.exchangeDeclare exchangeName, 'direct'
channel.queueDeclare queueName
channel.queueBind queueName, exchangeName, 'myRoutingKey'

def consumer = new QueueingConsumer(channel)
channel.basicConsume queueName, false, consumer

while (true) {
delivery = consumer.nextDelivery()
println "Received message: ${new String(delivery.body)}"
channel.basicAck delivery.envelope.deliveryTag, false
}
channel.close()
conn.close()

and publisher.groovy
import com.rabbitmq.client.*

@Grab(group='com.rabbitmq', module='amqp-client', version='1.7.2')
params = new ConnectionParameters(
username: 'guest',
password: 'guest',
virtualHost: '/',
requestedHeartbeat: 0
)
factory = new ConnectionFactory(params)
conn = factory.newConnection('lab.ndpar.com', 5672)
channel = conn.createChannel()

channel.basicPublish 'myExchange', 'myRoutingKey', null, "Hello, world!".bytes

channel.close()
conn.close()

Now start consumer in one terminal window
$ groovy consumer.groovy

and run publisher in another:
$ groovy publisher.groovy

On the consumer window you should see Received message: Hello, world! text, which means RabbitMQ works correctly.

Monitoring logs


You can check RabbitMQ logs by doing tail -f /var/log/rabbitmq/rabbit.log For example, starting the consumer results the following log entries:
=INFO REPORT==== 14-Mar-2010::11:20:53 ===
accepted TCP connection on 0.0.0.0:5672 from 192.168.2.10:62424

=INFO REPORT==== 14-Mar-2010::11:20:53 ===
starting TCP connection <0.24154.1> from 192.168.2.10:62424

Running the publisher:
=INFO REPORT==== 14-Mar-2010::11:22:08 ===
accepted TCP connection on 0.0.0.0:5672 from 192.168.2.10:62432

=INFO REPORT==== 14-Mar-2010::11:22:08 ===
starting TCP connection <0.24232.1> from 192.168.2.10:62432

=INFO REPORT==== 14-Mar-2010::11:22:08 ===
closing TCP connection <0.24232.1> from 192.168.2.10:62432

Now if we terminate the consumer by ^C there will be a warning
=WARNING REPORT==== 14-Mar-2010::11:25:03 ===
exception on TCP connection <0.24154.1> from 192.168.2.10:62424
connection_closed_abruptly

=INFO REPORT==== 14-Mar-2010::11:25:03 ===
closing TCP connection <0.24154.1> from 192.168.2.10:62424

but the connection is closed properly by the server.

That's it for now. Stay tuned for the future updates on my RabbitMQ experience.

Links


• Rapid application prototyping with Groovy DSL

Tuesday, February 23, 2010

Installing ejabberd on Ubuntu

Recently I've installed ejabberd server on Ubuntu box. Thanks to this nice document, the process was pretty straightforward. My experience was little bit different from the author's one, so I want to show here exact steps I did to make it work, maybe it will be helpful for you too.

The first step is to install the required package. You can use Synaptic Package Manager or just command line:

sudo apt-get install ejabberd

During the installation a new user, ejabberd, will be created in the system. This is the user the server will be running on. When installation is finished ejabberd server is started. To configure the server you need to stop it

sudo /etc/init.d/ejabberd stop

Next step is to configure administrator and hosts. Open /etc/ejabberd/ejabberd.cfg file for edit and make the following change

%% Admin user
{acl, admin, {user, "andrey", "jabber.ndpar.com"}}.
%% Hostname
{hosts, ["localhost", "ubuntu", "jabber.ndpar.com"]}.

For admin you need to specify the user name and domain name that you want to use as a Jabber ID. By default it's localhost and it's functional but it's better to change it to something meaningful. The list of hostnames is tricky. In theory you can provide there just localhost but in practice it didn't work for me. After digging into some Erlang exceptions I got while registering admin account (see next step) I came to conclusion that in the list of hostnames there must be a short hostname of the box. You can get it by running hostname -s command (in my case it was "ubuntu"). In addition you can provide other hostnames you like, but the short one is mandatory.

When you are done with editing, start the server

sudo /etc/init.d/ejabberd start

Now it's time to register the admin user we configured on the previous step. Run the following command replacing password placeholders with the actual password and providing user name and domain name from ejabberd.cfg file

sudo ejabberdctl register andrey jabber.ndpar.com xxxxxx

That's it! You have now working XMPP server with one registered user. To verify that everything is ok, in your browser go to the admin page of the server (http://jabber.ndpar.com:5280/admin) and check the statistics. You'll be asked to type your JID and password, so use the information you entered on the previous step



As a note, I didn't create my own SSL certificate because for isolated intranet the default one is quite enough. If you are not comfortable with that feel free to create a new certificate following the steps from the original article.

Now you are ready to add newly created account to your Jabber client. In Adium, for example, go to File -> Add Acount -> Jabber and provide server hostname/IP, JID and password.





Click OK button, accept security certificate permanently and go online.

Now, to really enjoy IM you need more users on your server. The best part here is that you can create new users just from your Jabber client. You can actually do many things from the client, and you don't need to ssh to the remote server and run command for that. Just go to File -> your ejabberd account, and chose whatever you need from the menu



Pretty cool, eh — client and admin tool in one place.

Wednesday, February 10, 2010

Multithreaded XmlSlurper

Groovy XmlSlurper is a nice tool to parse XML documents, mostly because of the elegant GPath dot-notation. But how efficient is XmlSlurper when it comes to parsing of thousands of XMLs per second? Let's do some simple test

class XmlParserTest {

static int iterations = 1000

def xml = """
<root>
<node1 aName='aValue'>
<node1.1 aName='aValue'>1.1</node1.1>
<node1.2 aName='aValue'>1.2</node1.2>
<node1.3 aName='aValue'>1.3</node1.3>
</node1>
<node2 aName='aValue'>
<node2.1 aName='aValue'>2.1</node2.1>
<node2.2 aName='aValue'>2.2</node2.2>
<node2.3 aName='aValue'>2.3</node2.3>
</node2>
<nodeN aName='aValue'>
<nodeN.1 aName='aValue'>N.1</nodeN.1>
<nodeN.2 aName='aValue'>N.2</nodeN.2>
<nodeN.3 aName='aValue'>N.3</nodeN.3>
</nodeN>
</root>
"""

def parseSequential() {
iterations.times {
def root = new XmlSlurper().parseText(xml)
assert 'aValue' == root.node1.@aName.toString()
}
}

@Test void testSequentialXmlParsing() {
long start = System.currentTimeMillis()
parseSequential()
long stop = System.currentTimeMillis()
println "${iterations} XML documents parsed sequentially in ${stop-start} ms"
}
}

I ran this test on my 4-core machine and I got

1000 XML documents parsed sequentially in 984 ms

Not really good (0.984 ms per document) but we didn't expect much from single threaded application. Let's parallelize this process

class XmlParserTest {
...
static int threadCount = 5
...
@Test void testParallelXmlParsing() {
def threads = []
long start = System.currentTimeMillis()
threadCount.times {
threads << Thread.start { parseSequential() }
}
threads.each { it.join() }
long stop = System.currentTimeMillis()
println "${threadCount * iterations} XML documents parsed parallelly by ${threadCount} threads in ${stop - start} ms"
}
}

And the result is

5000 XML documents parsed parallelly by 5 threads in 1750 ms

This is definitely better (0.35 ms per document) but doesn't look like parallel processing — the test time shouldn't increase in true parallelism.

The problem here is the default constructor of XmlSlurper. It does too much: first, it initializes XML parser factory loading bunch of classes; second, it creates new XML parser, which is quite expensive operation. Now imaging this happens thousand times per second.

Luckily, XmlSlurper has another constructor, with XML parser parameter, so we can create the parser up-front and pass it to the slurper. Unfortunately, we cannot reuse one parser instance between several slurpers because XML parser is not thread-safe — you have to finish parsing one document before you can use the same parser to parse another.

The solution here is to use preconfigured pool of parsers. Let's create one based on Apache commons-pool library.

public class XmlParserPoolableObjectFactory implements PoolableObjectFactory {
private SAXParserFactory parserFactory;

public XmlParserPoolableObjectFactory() {
parserFactory = SAXParserFactory.newInstance();
}
public Object makeObject() throws Exception {
return parserFactory.newSAXParser();
}
public boolean validateObject(Object obj) {
return true;
}
// Other methods left empty
}

public class XmlParserPool {
private final GenericObjectPool pool;

public XmlParserPool(int maxActive) {
pool = new GenericObjectPool(new XmlParserPoolableObjectFactory(), maxActive,
GenericObjectPool.WHEN_EXHAUSTED_BLOCK, 0);
}
public Object borrowObject() throws Exception {
return pool.borrowObject();
}
public void returnObject(Object obj) throws Exception {
pool.returnObject(obj);
}
}

Now we can change our test

class XmlParserTest {
static XmlParserPool parserPool = new XmlParserPool(1000)
...
def parseSequential() {
iterations.times {
def parser = parserPool.borrowObject()
def root = new XmlSlurper(parser).parseText(xml)
parserPool.returnObject(parser)
assert 'aValue' == root.node1.@aName.toString()
}
}
}

and run it again

1000 XML documents parsed sequentially in 203 ms
5000 XML documents parsed parallelly by 5 threads in 172 ms

That's much better (0.034 ms per document), and most importantly multi-threading really works now.

Resources

• Source code for this blog

• Article "Improve performance in your XML applications"

• GPath vs XPath

• commons-pool home page

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.

Friday, December 04, 2009

Ralph Johnson on learning to program

In general, there are 2 types of books. There is theory books and there is practical books. You can learn theory from a book. You cannot learn anything practical from a book. You learn practical things by practicing. Practical books are great, you read them, they tell you some ideas, but they tell you ideas of things to practice and you have to go off and practice it. It's only after you practice that you actually know. Then you'll say "Oh, now I know what that guy meant when he said that!"

That's how you learn. You only learn that stuff by doing. It's like any program - you can't actually learn to program by reading a book. Of course you read the book, but you have to get to the system and you write little programs and you write bigger and bigger programs and after a while you know how to program in the language, but you've got to do it to learn. You are not really going to learn by just reading the book.

When I was young I was a sort of person who listened to older people. Some young people don't, but I was a young person who did, but even so, they would tell me a lot of things. I would remember it, I wasn't like I disbelieved them, but it just a little bit hard to believe and I wasn't sure. Now, I'm over 50 and a lot of stuff makes an awful lot more sense to me and I say "Those old guys were right when telling me things", but you can't until you live it, until you go through it yourself, you really can't know it for sure.

When you get a pattern book, you need to be bold. You need to just try the stuff out and you need to expect you are going to make mistakes, too, so don't try it out when someone's life is on the line. You try it out in the privacy of your own home, where you are not going to damage anybody too much. I think that's actually a problem with industry - companies don't let people just experiment and play around. Everything is "This is production code, we're going live next week and here is a book, read that and figure out how to put it in there." It doesn't matter what the book is on - whether it's going to be a database, or parallel programming, you have to do it 3 or 4 times until you really get comfortable with it and people are always putting this code out there that they just barely figured out how to make it run. That's not smart. Managers should give people more time to get it right before they push it out to the world. Some do, but probably yours doesn't, if yours is like most of them.

watch the interview

Thursday, November 26, 2009

Finding first broken build with git-bisect

With git it's so easy to find which commit broke the build. Suppose you know that commit with tag release-1.0 was good, i.e. all tests passed. The latest commit, however, is failing.


So the build was broken somewhere in between release-1.0 and master HEAD. To find the exact one you just need to run two git commands. First

$ git bisect start HEAD release-1.0

Here you specify which commit you know is broken, and which one you remember was successful. After running this command you can see that git marked two commits with good and bad labels, and put the search starting point at the revision in the middle.


The second command is git bisect run cmd, where cmd is the script used as a criterion of successful build. If you use Maven then your command would be:

$ git bisect run mvn clean test

After you hit Enter button in the terminal, git starts running your criterion script using binary search algorithm. It might take time to finish this task, which depends on the number of revisions and the execution time of the script. Eventually it stops and shows you which commit caused the build failure.

7933e4658ea852754120fbc8fec34b2b85932e48 is first bad commit
commit 7933e4658ea852754120fbc8fec34b2b85932e48
Author: Andrey Paramonov
Date: Wed Nov 25 21:07:30 2009 -0500

Changed method implementation

:040000 040000 c0f3f9ef13d7daa4671205b9518c168a9ac10fe3 5a1cf3d6fb28d6f815c172319630b5d55ce4dc10 M src
bisect run success

If you look at the visual tool, you will spot the first bad commit by the label bisect/bad.


Resources

• git-bisect manual page

Thursday, November 12, 2009

State Machines in Erlang

There is some sort of confusion in the object-oriented community about functional languages. How is it possible to implement stateful application if the language has no concept of state? It turns out that it's actually quite possible, although the solution is completely deferent from what we see in the OO realm. In Erlang, for example, state can be implemented by using process messaging and tail recursion. This approach is so elegant that after you've learned it, the OO way of doing this looks unnatural. The code below is the Erlang implementation of Uncle Bob's FSM example. Look at it. Isn't that code clean and expressive? It looks almost like DSL but it's actually regular Erlang syntax.
-module(turnstile).
-export([start/0]).
-export([coin/0, pass/0]).
-export([init/0]).

start() -> register(turnstile, spawn(?MODULE, init, [])).

% Initial state

init() -> locked().

% Events

coin() -> turnstile ! coin.
pass() -> turnstile ! pass.

% States and transitions

locked() ->
receive
pass ->
alarm(),
locked();
coin ->
unlock(),
unlocked()
end.

unlocked() ->
receive
pass ->
lock(),
locked();
coin ->
thankyou(),
unlocked()
end.

% Actions

alarm() -> io:format("You shall not pass!~n").
unlock() -> io:format("Unlocking...~n").
lock() -> io:format("Locking...~n").
thankyou() -> io:format("Thank you for donation~n").

The idea behind this code is simple. Every state is implemented as a function that does two things: it listens for messages sent by other processes; when message is received the appropriate action is taken and one of the state-functions called recursively. Simple, right? And thread-safe!

Thursday, October 22, 2009

Parsing files using Groovy regex

In my previous post I mentioned several ways of defining regular expressions in Groovy. Here I want to show how we can use Groovy regex to find/replace data in the files.

Parsing properties file (simplified)1

Data: each line in the file has the same structure; the entire line can be matched by single regex. Problem: transform each line to the object. Solution: construct regex with capturing parentheses, apply it to each line, extract captured data. Demonstrates: File.eachLine method, matrix syntax of Matcher object.

def properties = [:]
new File('path/to/some.properties').eachLine { line ->
if ((matcher = line =~ /^([^#=].*?)=(.+)$/)) {
properties[matcher[0][1]] = matcher[0][2]
}
}
println properties

Parsing csv files (simplified)2

Data: each line in the file has the same structure; the line consists of the blocks separated by some character sequence. Problem: transform each line to the list of objects. Solution: construct regex with capturing parentheses, parse each line with the regex in a loop extracting captured data. Demonstrates: ~// Pattern defenition, Matcher.group method, \G regex meta-sequence.

def regex = ~/\G(?:^|,)(?:"([^"]*+)"|([^",]*+))/
new File('path/to/file.csv').eachLine { line ->
def fields = []
def matcher = regex.matcher(line)
while (matcher.find()) {
fields << (matcher.group(1) ?: matcher.group(2))
}
println fields
}

Finding snapshot dependencies in the pom (simplified)3

Data: file contains blocks with known boundaries (possibly crossing multiple lines). Problem: extract the blocks satisfying some criteria. Solution: read the entire file into the string, construct regex with capturing parentheses, apply the regex to the string in a loop. Demonstrates: File.text property, list syntaxt of Matcher object, named capture, global \x regex modifier, local \s regex modifier.

def pom = new File('path/to/pom.xml').text
def matcher = pom =~ '''(?x)
<dependency> \\s*
<groupId>([^<]+)</groupId> \\s*
<artifactId>([^<]+)</artifactId> \\s*
<version>(.+?-SNAPSHOT)</version> (?s:.*?)
</dependency>
'''
matcher.each { matched, groupId, artifactId, version ->
println "$groupId:$artifactId:$version"
}

Finding stacktraces in the log

Data: file contains entries each of which starts with the same pattern and can span multiple lines. Typical example is log4j log files:

2009-10-16 15:32:12,157 DEBUG [com.ndpar.web.RequestProcessor] Loading user
2009-10-16 15:32:13,258 ERROR [com.ndpar.web.UserController] id to load is required for loading
java.lang.IllegalArgumentException: id to load is required for loading
at org.hibernate.event.LoadEvent.(LoadEvent.java:74)
at org.hibernate.event.LoadEvent.(LoadEvent.java:56)
at org.hibernate.impl.SessionImpl.get(SessionImpl.java:839)
at org.hibernate.impl.SessionImpl.get(SessionImpl.java:835)
at org.springframework.orm.hibernate3.HibernateTemplate$1.doInHibernate(HibernateTemplate.java:531)
at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:419)
at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374)
at org.springframework.orm.hibernate3.HibernateTemplate.get(HibernateTemplate.java:525)
at org.springframework.orm.hibernate3.HibernateTemplate.get(HibernateTemplate.java:519)
at com.ndpar.dao.UserManager.getUser(UserManager.java:90)
... 62 more
2009-10-16 15:32:14,659 DEBUG [com.ndpar.jms.MessageListener] Received message:
... multi-line message ...
2009-10-16 15:32:15,169 INFO [com.ndpar.dao.UserManager] User: ...

Problem: find entries satisfying some criteria. Solution: read the entire file into the string4, construct regex with capturing parentheses and lookahead, split the string into entries, loop through the result and apply criteria to each entry. Demonstrates: regex interpolation, combined global regex modifiers \s and \m.

def log = new File('path/to/your.log').text
def logLineStart = /^\d{4}-\d{2}-\d{2}/
def splitter = log =~ """(?xms)
( ${logLineStart} .*?)
(?= ${logLineStart} | \\Z)
"""
splitter.each { matched, entry ->
if (entry =~ /(?m)^(?:\t| {8})at/) println entry
}

Replacing text in the file

Use Groovy one-liner to perform the replacement. Here is the Tim's example in Groovy:

$ groovy -p -i -e '(line =~ /1\.6/).replaceAll("2.0-alpha-1-SNAPSHOT")' `find . -name pom.xml`


Resources

• Groovy regexes
• Groovy one-liners
• Using String.replaceAll method

Footnotes

1. This example is for demonstration purposes only. In real program you would just use Properties.load method.
2. The regex is simplified. If you want the real one, take a look at Jeffrey Friedl's example.
3. Again, in reality you would find snapshots using mvn dependency:resolve | grep SNAPSHOT command.
4. This approach won't work for big files. Take a look at this script for practical solution.

Wednesday, October 14, 2009

GParallelizer Performance

GParallelizer is a Groovy wrapper for new Java concurrency library. It allows you to perform list and map operations using parallel threads, which in theory leverages the full power of multi-processor computations. Here I want to check if it's true in reality. I run the following tests on my dual-core MacBook

import static org.gparallelizer.Parallelizer.*
import org.gparallelizer.ParallelEnhancer
import org.junit.Before
import org.junit.Test

class GParsTest {

def list = []

@Before void setUp() {
1000000.times {
list << (float) Math.random()
}
}

@Test void sequential() {
def start = System.currentTimeMillis()
list.findAll { it < 0.4 }
def duration = System.currentTimeMillis() - start

println "Sequential: ${duration}ms"
}

@Test void parallel_with_enhancer() {
ParallelEnhancer.enhanceInstance list

def start = System.currentTimeMillis()
list.findAllAsync { it < 0.4 }
def duration = System.currentTimeMillis() - start

println "Parallel with enhancer: ${duration}ms"
}

@Test void parallel_with_parallelizer_2() {
parallelWithParallelizer 2
}

@Test void parallel_with_parallelizer_3() {
parallelWithParallelizer 3
}

@Test void parallel_with_parallelizer_5() {
parallelWithParallelizer 5
}

@Test void parallel_with_parallelizer_10() {
parallelWithParallelizer 10
}

def parallelWithParallelizer(threads) {
def start = System.currentTimeMillis()
withParallelizer(threads) {
list.findAllAsync { it < 0.4 }
}
def duration = System.currentTimeMillis() - start

println "Parallel with parallelizer (${threads}): ${duration}ms"
}
}

And here is the output

Sequential: 774ms
Parallel with enhancer: 9311ms
Parallel with parallelizer (2): 1785ms
Parallel with parallelizer (3): 769ms
Parallel with parallelizer (5): 500ms
Parallel with parallelizer (10): 722ms

Something strange happened with mixed-in ParallelEnhancer, but with Parallelizer performance improved indeed. With optimal thread pool size parallel processing is 35% faster than sequential.

Conclusion: Use GPars methods if you need to process big amount of data. Try different config parameters to find the best solution for your particular problem.

Resources

• Brian Goetz on new concurrency library
• Vaclav Pech on GPars

Tuesday, October 06, 2009

Converting XML to POGO

Suppose we want to convert XML to Groovy bean:

class MyBean {
String strField
float floatField
int intField
boolean boolField
}

def message = "<xml stringAttr='String Value' boolAttr='true' />"

def xml = new XmlSlurper().parseText(message)

def bean = new MyBean(
strField: xml.@stringAttr,
boolField: xml.@boolAttr
)

Everything looks good, even assertions succeed

assert 'String Value' == bean.strField
assert bean.boolField

Now let's try false value:

message = "<xml stringAttr='String Value' boolAttr='false' />"
assert !bean.boolField

Oops, the assertion failed. Why? Because xml.@boolAttr cast to boolean always returns true. The correct implementation must be like this:

message = "<xml stringAttr='String Value' floatAttr='3.14' intAttr='9' boolAttr='false' />"

xml = new XmlSlurper().parseText(message)

bean = new MyBean(
strField: xml.@stringAttr.toString(),
floatField: xml.@floatAttr.toFloat(),
intField: xml.@intAttr.toInteger(),
boolField: xml.@boolAttr.toBoolean()
)
assert 'String Value' == bean.strField
assert 3.14F == bean.floatField
assert 9 == bean.intField
assert !bean.boolField

Now everything works properly. The moral of this blog post: Create more unit tests (assertions), especially when you work with dynamic language.

Resources

• Converting String to Boolean

Friday, September 25, 2009

Building regular expressions in Groovy

Because of compact syntax regular expressions in Groovy are more readable than in Java. Here is how Jeffrey Friedl's example would look like in Groovy:

def subDomain  = '(?i:[a-z0-9]|[a-z0-9][-a-z0-9]*[a-z0-9])' // simple regex in single quotes
def topDomains = """
(?x-i : com \\b # you can put whitespaces and comments
| edu \\b # inside regex in eXtended mode
| biz \\b
| in(?:t|fo) \\b # but you have to escape
| mil \\b # backslashes in multiline strings
| net \\b
| org \\b
| [a-z][a-z] \\b
)"""

def hostname = /(?:${subDomain}\.)${topDomains}/ // variable substitution in slashy strings

def NOT_IN = /;\"'<>()\[\]{}\s\x7F-\xFF/ // backslash is not escaped in slashy strings
def NOT_END = /!.,?/
def ANYWHERE = /[^${NOT_IN}${NOT_END}]/
def EMBEDDED = /[$NOT_END]/ // you can ommit {} around var name

def urlPath = "/$ANYWHERE*($EMBEDDED+$ANYWHERE+)*"

def url =
"""(?x:
\\b

# match the hostname part
(
(?: ftp | http s? ): // [-\\w]+(\\.\\w[-\\w]*)+
|
$hostname
)

# allow optional port
(?: :\\d+ )?

# rest of url is optional, and begins with /
(?: $urlPath )?
)"""

assert 'http://www.google.com/search?rls=en&q=regex&ie=UTF-8&oe=UTF-8' ==~ url

As you can see, there are several options, and for every subexpression you can choose the one that's more expressive.

Resources

• Martin Fowler on composed regexes
• Pragmatic Dave on regexes in Ruby
• Feature request to make regexes even groovier
• Mastering Regular Expressions — best regex book
• Groovy Pattern and Matcher classes

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, August 20, 2009

Measuring LiveCycle Performance: Message Size

The method of measuring performance provided by LCDS works only in situations when producer and consumer of messages are both on the Flex side. For Data Services that means you can obtain some metrics only for initial collection fill:

Original message size(B): 499
Response message size(B): 17687
Total time (s): -1250809384.8
Network Roundtrip time (s): -1250809384.868
Server processing time (s): 0.068
Server adapter time (s): 0.014
Server non-adapter time (s): 0.054

If you want to know message size and response time for messages pushed from Java server to Flex client, this method doesn't help* in the current version of LCDS (2.6.1). Adobe promised to add this feature in the future release but for now you have to use other methods. Here is what I use to measure message size.

1. JMX. By default LCDS exposes some useful metrics through JMX:



2. Flex log. If you enable log in the services-config.xml, you will see something like this in the output console for every data push:

Thread[1563082333@qtp0-0,5,main] registering write interest for Connection '1752654181'.
Thread[my-rtmp-SocketServer-Reactor1,5,main] unregistering write interest for Connection '1752654181'.
Thread[my-rtmp-SocketServer-Reactor1Writer,5,main] Connection '1752654181' starting a write.
Thread[my-rtmp-SocketServer-Reactor1Writer,5,main] chunk output stream writing message; ack state: 3
...
Thread[my-rtmp-SocketServer-Reactor1Writer,5,main] Connection '1752654181' finished a write. 233 bytes were written.

3. If you don't have access to the server, you can use any network protocol analyzer (WireShark is really good) on the client side to monitor size of packets received from the server.

* Actually, there is one undocumented feature that can be used with the described method to measure size of "create" messages, but Adobe does not recommend to use it.

Resources

Part 1: Measuring LiveCycle Performance: Errors

Thursday, August 13, 2009

Git on Cygwin

Here are the steps I perform to use Git on Cygwin:

• Install necessary packages: git, gitk, subversion, subversion-perl. That's because I use Subversion via Git bridge.

• Amend C:\cygwin\lib\perl5\vendor_perl\5.xx\Git.pm file using this patch. That's to fix small bug causing "Permission denied: Can't open '/cygdrive/c/DOCUME~1/myself/LOCALS~1/Temp/report.tmp'" error.

• Configure white spaces using this command to fix trailing spaces warnings.

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.

Monday, July 20, 2009

Tom DeMarco on Software Engineering

Software development is inherently different from a natural science such as physics, and its metrics are accordingly much less precise in capturing the things they set out to describe.
...
Strict control is something that matters a lot on relatively useless projects and much less on useful projects. It suggests that the more you focus on control, the more likely you’re working on a project that’s striving to deliver something of relatively minor value.
To my mind, the question that’s much more important than how to control a software project is, why on earth are we doing so many projects that deliver such marginal value?
...
So, how do you manage a project without controlling it? Well, you manage the people and control the time and money. You say to your team leads, for example, “I have a finish date in mind, and I’m not even going to share it with you. When I come in one day and tell you the project will end in one week, you have to be ready to package up and deliver what you’ve got as the final product. Your job is to go about the project incrementally, adding pieces to the whole in the order of their relative value, and doing integration and documentation and acceptance testing incrementally as you go.”
...
For the past 40 years we’ve tortured ourselves over our inability to finish a software project on time and on budget. But this never should have been the supreme goal. The more important goal is transformation, creating software that changes the world or that transforms a company or how it does business.

read the whole article

Friday, July 17, 2009

Michael Feathers on Functional Programming

One of the things that seems like a rather pessimistic observation, but I think it's true to a degree, that the number of programmers who are able to or willing to think in a mathematically sophisticated way about code is relatively small, in comparison to the total population of programmers. I think that even though functional programming is becoming more popular, it is a bit of uphill battle for the industry and it may become just a very strong good niche tool for the people who are able to use it very well. I'm glad to see it's being brought up in prominence now, but I'm wondering if we'll ever see a day when everybody is doing work in functional programming. On the other hand, we got to the point where closures are becoming part of practically every programming language. It only took 30 years, so there is hope, I guess.

watch the interview

Thursday, July 16, 2009

Spring integration tests with mocked collaborators

Sometimes when you write integration tests that load Spring application context, you want to mock some collaborators just to verify that they've been called with correct data. This is a quite legitimate approach, but you have to keep in mind that Spring caches application contexts during the test execution. That means, after you replace a bean with a mock, all subsequent tests that share the same application context will use mocked instance instead of real one. That might cause tests to fail in very peculiar way. For example, test succeeds in Eclipse but fails in command line; you create new test and the old one starts failing, etc.

To fix the problem, you need to reload application context after every test that uses mocks. The best way to do this is to use @DirtiesContext annotation. In Spring 2.5 this was a method level annotation, but starting with Spring 3.0RC1 you can use it on the class level (thanks Spring!). So the rule of thumb is:

If you mock a bean in the Spring integration test, annotate the test class with @DirtiesContext

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:appclicationContext.xml"})
@DirtiesContext
public class IntegrationTest {

@Autowired
private Application application;

private Collaborator mockCollaborator;

@Before
public void setUp() {
mockCollaborator = mock(Collaborator.class);
application.setCollaborator(mockCollaborator);

}

@Test
public void collaborator_is_called_once() {
...
verify(mockCollaborator, times(1)).methodCall(...);
}
}

Resources

Source files for this post
• Spring annotations documentation
Class level @DirtiesContext annotation
• How to build Spring 3

Tuesday, July 07, 2009

Iteration Length

Iteration length in agile software development is the time between two consecutive customer feedbacks.

You can set up iteration length to 2 weeks in the JIRA, but if you meet your customer twice a year then your iteration length is 6 months. You can do iteration demo every 2 weeks, but if nobody except your manager sees the demo then your iteration length is not 2 weeks. You can even release some artifacts and change the version of your application every 2 weeks, but if your customer never tried to use your application then your iteration length is definitely not 2 weeks.

In general, without customer feedback there is no iteration. It's a cascade.

Monday, July 06, 2009

Tony Hoare: 50 Years with Legacy Code

Q: With your time in the computer science field, I'm guessing that you've seen some common trends, things that have remained consistent throughout time since 1960. What are those trends that have remained consistent and how do you think it will continue to the future?

A: I'm sorry to say it's the mistakes that remained consistent, not the virtues. The problems that afflicted us in the 1960s were the difficulty of predicting performance of large systems, the difficulty of discovering requirements, the difficulty of implementing code that was coherent across large-scale module boundaries. All of these things are still with us. I suppose I could say that even in 1960 living with legacy code was there. Dykstra once said that every day when he comes into work he thanks providence that he is not charged with the responsibility of living with legacy code - that's certainly still with us.

watch the interview

Friday, July 03, 2009

Collaborating Using git-svn

I like Git, and I'm using it everywhere. It gives you so much power that once you taste it, you won't want to come back to traditional source control systems. One of the Git benefits is collaboration friendliness. Git encourages collaboration, ideas exchange, and code review. If your team is using Git then you know how easy it is to share your code with your co-workers. But sometimes you are the only person in the team who uses Git, and everybody else is on Subversion. Don't worry, you still can share your ideas by means of git-svn tool, and here I want to show you how. The process is not as simple as native Git (via pull/push or patch/apply) but it's better than nothing.

Suppose you have an idea and you are eager to try it. You don't want to create a branch in Subversion because you don't know if your idea will work out, and committing all your crazy stuff in Subversion can easily pollute it. So you create a local Git branch and start working.

$ git checkout -b topic/great-idea

You code, test, git-add, git-commit, code, test, etc. At some point you see that your idea was great indeed and it's time to show the amazing results to your teammates. Now you need to "push" your Git branch to Subversion. To do this you have to create Subversion brunch first

$ svn copy http://svn.repo.path/project-name/trunk \
http://svn.repo.path/project-name/branches/great-idea \
-m "Created branch for my cool stuff"

Next step is to add this Subversion branch as a remote branch to Git configuration. Open .git/config file with a text editor. You should see something like this

[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[svn-remote "svn"]
url = http://svn.repo.path
fetch = project-name/trunk:refs/remotes/trunk

If you built your Git repository by cloning Subversion repository (which you most likely did), you will have one or more svn-remote sections in this configuration file. You need to add another one for new Subversion branch.

[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[svn-remote "svn"]
url = http://svn.repo.path
fetch = project-name/trunk:refs/remotes/trunk
[svn-remote "svn-great-idea"]
url = http://svn.repo.path
fetch = project-name/branches/great-idea:refs/remotes/great-idea

Next step is to fetch Subversion branch, but first you need to know revision number when this branch was created. Run this command

$ svn log --stop-on-copy http://svn.repo.path/project-name/branches/great-idea

You should get something like this:

------------------------------------------------------------------------
r2165 | andy | 2009-07-03 14:36:59 -0400 (Fri, 03 Jul 2009) | 1 line

Created branch for my cool stuff
------------------------------------------------------------------------

The first number of the output is what we are looking for:

$ git svn fetch svn-great-idea -r2165

Now you have the Subversion branch in you Git. If you run git branch -a command you will see "great-idea" branch in the list.

 master
* topic/great-idea
great-idea
trunk

You shouldn't work on remote branch, so let's create local one:

$ git branch svn-branch/great-idea remotes/great-idea

I put it in svn-branch namespace just to make it visually clear that this branch is in synch with Subversion. The next set of commands is a standard way to bring your work from one local branch to another. In our case: from initial topic/great-idea to svn-branch/great-idea

$ git rebase svn-branch/great-idea
$ git checkout svn-branch/great-idea
$ git rebase topic/great-idea

That's it. Now you are ready to commit your code to Subversion:

$ git svn dcommit

Done. As an option, you can delete initial branch because you have a Subversion backed copy of it:

$ git branch -D topic/great-idea

Resources

• Ian Boston's post explaining how to add Subversion branches to Git.

Tuesday, June 30, 2009

Measuring LiveCycle Performance: Errors

There are several ways to measure LiveCycle performance. One of them is to call appropriate method on MessagePerformanceUtils class. This approach is pretty straightforward but sometimes you might get an error:

Destination '...' either does not exist or the destination has no channels defined (and the application does not define any default channels.)

That means your are using statically configured channels and you don't package services configuration into the SWF file. To fix it, in the Flex Builder add config files folder to Flex source path and specify 'services' compiler argument:

 

Although it solves the problem, this approach is not suitable for real project as you don't want to compile SWF file with hard coded services configuration. Instead of that you would create dynamic channels on the client side, and configure them using IoC framework (i.e. Parsley, Prana or your own). And if you do that you will most likely get the following error:

Error: Message is missing MPI headers. Verify that all participants have it enabled

The reason of that is: you configured MPI headers only on the server side, but not on the Flex side. To fix it, you need to set recordMessageTimes and recordMessageSizes properties of Channel class to true. The problem is that those properties are read-only, so you cannot assign them to any value directly. But here is a trick: you can use applySettings() method:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.messaging.Channel;
import mx.messaging.ChannelSet;
import mx.messaging.channels.RTMPChannel;
import mx.messaging.events.MessageEvent;
import mx.messaging.messages.MessagePerformanceUtils;

private function init():void {
ds.channelSet = createChannelSet();
}

private function createChannelSet():ChannelSet {
var channels:Array = new Array();
channels.push(createRtmpChannel());

var result:ChannelSet = new ChannelSet();
result.channels = channels;
return result;
}

private function createRtmpChannel():Channel {
var result:Channel = ... // get it from IoC
result.applySettings(customSettings());
return result;
}

private function customSettings():XML {
return <channel-definition>
<properties>
<record-message-times>true</record-message-times>
<record-message-sizes>true</record-message-sizes>
</properties>
</channel-definition>;
}


private function messageHandler(event:MessageEvent):void {
var performanceUtils:MessagePerformanceUtils = new MessagePerformanceUtils(event.message);
statistics.text = performanceUtils.prettyPrint();
}
]]>
</mx:Script>

<mx:DataService id="ds" destination="MyDestination" result="messageHandler(event)" />
<mx:ArrayCollection id="domainObjects" />
<mx:TextArea id="statistics" />
</mx:Application>

Resources

• Check out example sources from GitHub.

Wednesday, June 21, 2006

Let's get started

This blog becomes a successor to my jroller.