My First (Strophe) Air App


First Attempt at writing an AIR (Adobe Integrated Runtime) application. It’s a simple chat “widget” that users install and allow to chat with “Jane”, who can be a “helpdesk” or “expert” or “customer service”. I call it “VJ”.

The environment that I chose to run it after some searching and recommendations is:

  1. Open Fire – This is really the chat server using XMPP (jabber)
  2. StropheJS – This is the XMPP JS library that I choose to use.

Setting up Open Fire

Setting up Open Fire was relatively easy. I basically followed the steps from the documentation and help from Malcom .

However 2 things complicate matters.

  1. I am running Open Fire behind a firewall so needed to set up a Reverse Proxy in order to configure it.
  2. VJ’s maybe running behind a firewall too. So Open Fire needs to be able to communicate on port 80 as well. Luckily Open Fire supports “http-bind” so I just need to set it up.

Reverse Proxy for Open Fire and HTTP-BIND

In order to configure Open Fire, I need to access the port 9090. But I can’t change the firewall. So instead, set up a Virtual Host to (Reverse) proxy it. This is also where I administer my Open Fire server.



ProxyRequests Off
ProxyPass /
ProxyPassReverse /

Now I can configure Open Fire by going to without the 9090 port reversed proxied. example

Important (for HTTP-BIND)

Configure the Server/Domain Name to be Another sub-domain – instead of, use because

  1. openfire.mydomain. com is reverse proxied for administration only and is the server/domain of the XMPP server that clients will connect to.
  2. So needs 5222 open for chat clients talking XMPP. But because I am behind a firewall and chat clients may also be behind a firewall, we cannot use 5222 to communicate.
  3. Luckily Open Fire has HTTP-BIND, that means it can talk HTTP that wraps the XMPP. But HTTP-BIND runs on port 7070.
  4. Since I cannot open 7070 in the firewall. I need to Reverse Proxy this as well so that clients can use a normal looking URL to talk HTTP(XMPP) to.



ProxyRequests Off
ProxyPass /http-bind
ProxyPassReverse /http-bind



I need also a web chat client for JANE, the “helpdesk” agent. so I installed Spark Web. Installing it is straight forward too. download it and put it in the server.

<Directory "/usr/local/sparkweb">
AllowOverride All
Order deny,allow

and edit the index.html

function jive_sparkweb_getConfig()
return {
server: "localhost",
connectionType: "http",
port: "80"

Now Start Coding!!

I use these for development.

  1. Eclipse IDE
  2. Aptana Studio

With the help of the samples from Aptana, I was able to get started with a chrome window for my chat widget. However, the main task for me here is to integrate with the Strophe JS.

Integrate with Strophe.js

Basically I modified echobot.js since it already provides the Login and onMessage Handler.

First include all the necessary Strophe .js files.

<script language='javascript' type='text/javascript' src='lib/strophejs/strophe.js'></script>

BOSH SERVICE (credit to Malcom)

specify the BOSH Service url

var BOSH_SERVICE = "";


The login function is straight forward, except I am fixing the domain so that the client (user) need not remember it.

var DOMAIN = "";


connection.connect($(‘#jid’).get(0).value + “@” + DOMAIN, $(‘#pass’).get(0).value, onConnect);


Send Message

  • This function just sends the message, here I pass in the elemid which is a textarea.
  • and JANE is my “helpdesk” so this widget only can only chat with her.
  • sends a .c(“body”).t(text) instead of cnode(body)
  • clears the textarea

var JANE = "";

function sendMessage(elemid){

if (connection.connected && connection.authenticated) {
var text = $('#' + elemid).get(0).value;
if (text.length > 0) {
var from = Strophe.getNodeFromJid(connection.jid);
var to = JANE;
var reply = $msg({
to: JANE,
from: connection.jid,
type: "chat"

log(text, "from");

$('#' + elemid).get(0).value = "";

else {
log("You have to log in before you can ask Jane");

Receive Message

  • This function is a registered handler when a message arrives

function onMessage(msg){
var to = msg.getAttribute('to');
var from = msg.getAttribute('from');
var type = msg.getAttribute('type');
var elems = msg.getElementsByTagName('body');

if (type == "chat" && elems.length > 0) {

var body = elems[0];
log(Strophe.getText(body), "to");

return true;


mvn release:*

1 Comment

I am trying to use maven release plugin to cut releases, I really like maven’s dependency management, and here’s trying to use maven to manage releases. Special thanks to John and Nick.

It’s really just 3 steps :

  1. mvn release:prepare -DdryRun=true
  2. mvn release:clean release:prepare
  3. mvn release:perform

But here are some things I learnt though…

  1. Upon release:prepare errors, use release:rollback before release:clean (wipe out all the pom.* that’s needed for release plugin)
  2. Undoing subversion changes using  svn merge -r 999:998 http://subversion/project/dir to revert back to the last working version. This is because release:prepare errors might have occurred after copy checked into subversion.

And finally, some pre-requisites :

Working copy must be SNAPSHOT

Dependencies must NOT be SNAPSHOT – to overcome this I had to  download and install a ‘special version’ into your own (local) maven repository :


will deploy to my scm definition in my pom.xml


and reference to it in your repository


Mac OS subversion 1.4.4 – use this version because on Leopard, subversion 1.5.1 has a problem and somehow can’t create tags on subversion causing the release:prepare to check in the release copy but cannot move/create a tag copy – that’s where I learn (2) above – see here also

Firefox autocomplete on HTML Form

Leave a comment

Just learn t something today…

FireFox will cache your form values when you refresh a page…. I had this problem and was causing my animation to go haywire…. if you need to turn this auto caching off you can do :

<form autocomplete="off" /> or <input ... autocomplete="off" />

Hibernate 3 + MySQL 5 + XDoclet2 + HBM2DDL

Leave a comment

It all started with this error message :

JDBCExceptionReporter - SQL Error: 0, SQLState: 01004
[14 Feb 2007 00:26] ERROR - JDBCExceptionReporter - Data truncation: Out of range value adjusted for column 'USER_STREAMING' at row 1

further investigation found that only “Boolean” property was having this problem. I was using MySQL 5, and hbm2ddl was mapping a “Boolean” property I had on my POJO to a “Bit”, before (on MySQL4) the hbm2ddl Ant task was mapping “Boolean” into “TinyInt(1)”

Changing the column type from “Bit” to “TinyInt(1)” manually on MySQL,  made the error go away. However, I still wanted to avoid manually changing my generated ddl script… I really like xdoclet/hbm2ddl combination (annotations are even better… but later..) and according to this you cn use the “sql-type” to specify the sql type that hbm2ddl can pick up.

So in all, I just had to change my xdoclet tags to:

* @hibernate.column name="USER_

That’s all…

Hibernate Annotations – Bidirectional One-To-Many


I was trying to use Hibernate Annotations, which I think is great … I am happy with it… but I learnt some stuff which I know I will forget. So I am blogging it here, hopefully I will make sense and serve as a reminder for me when I need it. Maybe it might help someone else too… who knows…

Bidirectional One-To-Many
here I am trying to create a Bidirectional One-To-Many relationship between Question and Choice, a Question has many choices.

  1. Setup entities

    @Table (name = "QUESTION")
    public class Question implements Serializable

    @GeneratedValue (strategy = GenerationType.AUTO)
    @Column (name = “QUESTION_ID”)
    private Long id;

    @Column (name = “TEXT”)
    private String text;

    @Table (name = "CHOICE")
    public class Choice implements Serializable

    @GeneratedValue (strategy = GenerationType.AUTO)
    @Column (name = “CHOICE_ID”)
    private Long id;

    @Column (name = “TEXT”)
    private String text;

  2. Setup the ONE side on Question – I read ONE Question has MANY CHOICE(S).

    @OneToMany (mappedBy="question")
    private Set choices = new HashSet();

    mappedBy – means “I am not the owner side”, I am mapped by question from the other side of the relationship. It will also not create the database column which makes sense, I would expect a foreign key on the CHOICE table instead.

  3. Setup the Many side on Choice – I read on MANY Choice has ONE Question
    @JoinColumn (name="QUESTION_ID")
    private Question question;

    the @JoinColumn indicate the owning side of the relationship, it is responsible for updating the database column. It will create the QUESTION_ID column on the CHOICE table

  4. Reversing the Relationship so that the owning side is the Question instead

    @JoinColumn (name = "QUESTION_ID")
    private Set choices = new HashSet();


    @JoinColumn (name="QUESTION_ID", updatable = false, insertable = false)
    private Question question;

  5. Finally I need to use a List instead of Set – so I can preserve the order of the Choice(s).Question

    @OneToMany (
    cascade = {CascadeType.ALL},
    fetch = FetchType.EAGER
    @JoinColumn (name = "QUESTION_ID")
    value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN
    @org.hibernate.annotations.IndexColumn(name = "CHOICE_POSITION")
    private List choices = new ArrayList();


    @JoinColumn (name="QUESTION_ID", nullable = false, updatable = false, insertable = false)
    private Question question;

    the @org.hibernate.annotations.IndexColumn defines the colum CHOICE_POSITION that will be used to maintain the order of the list. Some how reversing the ownership is the only way to get the IndexColumn to work