1

I'm dealing with creating a web service which will receive requests to send messages via XMPP. However, all messages will be sent from one account (the server logs in and sends notifications to users).

Here comes the problem - how to implement it? I was trying to implement the XMPPConnection class as a singleton, but I got stuck at passing arguments to constructors containing the hostname, port, JID etc

As I've read here, a singleton with parameters is not a singleton... Hence, I thought about solving it as follows (is it some kind of factory?):

public class XMPPConnectionSingleton
{
    private volatile static XMPPConnectionSingleton anInstance;
    private volatile static XMPPConnection connection;

    public static XMPPConnectionSingleton getInstance() {
        if(anInstance == null) {
            synchronized (XMPPConnectionSingleton.class) {
                if(anInstance == null)
                    anInstance = new XMPPConnectionSingleton();
            }
        }
        return anInstance;
    }

    public void init(String server, int port, String jid, String password, String resource)
    {
        ConnectionConfiguration conf = new ConnectionConfiguration(server, port);
        connection = new XMPPConnection(conf);
        // logging in, etc.
    }
}

Is it a good way to go? Or maybe it is better to make a wrapping class for XMPPConnection, accepting a constructor with no parameters?

Community
  • 1
  • 1
Maciej Papież
  • 431
  • 1
  • 6
  • 20
  • A key issue is how your web service will work, do you expect several call simultaniously? And if so, does the XMPP server allow for multiple connected clients at the same time? – ThomasRS Sep 28 '11 at 16:57
  • Simultaneous calls may occur, as the web service will be used in environments of different sizes. What do you mean by multiple connected clients at the same time? The XMPP server has to accept only one connection from the WS server. – Maciej Papież Sep 28 '11 at 18:34
  • Yeah well according to other questions [here](http://stackoverflow.com/search?q=XMPP+multiple+clients+), it might seem like multiple clients at the same time is possible. If not too much overhead, you could consider going that way. – ThomasRS Sep 28 '11 at 20:06
  • @Thomas I know now, what you mean by multiple connections simultaneously - that I shouldn't bother about single instances and create a separate connection for each WS request, right? I don't like this overhead, even creating a connection takes some time much more than few ms... Answering your second question - no, it's not static. It is a member of XMPPconnection class. – Maciej Papież Sep 28 '11 at 20:22
  • Okey see response. And for login, I was thinking of server, port, jid, password? – ThomasRS Sep 28 '11 at 20:43
  • @Maciej Papiez: not an answer (hence the comment) but... That DCL (double-checked locking) is working in that it uses *volatile* but hences kinda defeats the whole purpose of the DCL (whose purpose is to avoid synchronization costs). It defeats the purpose because *volatile* introduces synchronization costs. What you *think* you achieved there (and didn't achieve) can correctly be achieved using the *"initialize-on-demand holder class"* idiom (described for example in the book *Effective Java*). – SyntaxT3rr0r Sep 29 '11 at 13:34
  • @SyntaxT3rr0r The code I used - isn't it the 4th listing from this [Wikipedia article](http://en.wikipedia.org/wiki/Double-checked_locking)? – Maciej Papież Sep 29 '11 at 14:18
  • @Maciej Papiez: it's precisely that one... And it's exactly as I wrote ; ) It's not because "it works" under the new memory model that it is "correct". As I wrote the whole point of the DCL is to **avoid** synchronization costs. You're not avoiding synchronization costs if you use *volatile* because volatile does incur, under the hood, a penalty. Right under the 4th listing on the Wikipedia article you linked to, the real correct way to do it is explained: the *" initialization on demand holder idiom"*. : ) – SyntaxT3rr0r Sep 29 '11 at 14:25
  • @SyntaxT3rr0r All right, I understand why the working solution is not the correct solution:) thanks for comment and pointing the direction. – Maciej Papież Sep 29 '11 at 14:36

2 Answers2

0

You can use a ~singleton, read this question and the related pages here and here. The cleanest is to initialize in a static block, but that is obiously not always possible.

The obious, simple, approach is to synchronize on a single instance and all WS calls wait. This will work great for low capacity. Using ReentrantLock might enhance that logic somewhat - for example sending a timeout WS reponse rather than having the whole HTTP call out.

If you need many concurrent sessions, consider checking out whether the server can log in with multiple clients and then initialize as many clients as you want - then use the ReentrantLock in some way to determine which is currently free.

Depending on the logic in your WS calls (does it need confirmation that message is sent to XMPP?) you might consider queueing up the calls and serving them from another thread, returning the WS call before the actual message is sent (http hold time is normally 30 second, but can be changed).

Might even be possible to let each WS session connect and have its own session for the duration of the call.

Edit:I suggest you go with

// Correct lazy initialization in Java 
@ThreadSafe
class Foo {
    private static class HelperHolder {
       public static Helper helper = new Helper();
    }

    public static Helper getHelper() {
        return HelperHolder.helper;
    }
}

where Helper is

  1. a helper class which pull the login info from somewhere, wraps the XMPP client class, or
  2. the XMPP client class itself with hardcoded login info.

Then simply use synchronized operator on the returned object.

Community
  • 1
  • 1
ThomasRS
  • 8,215
  • 5
  • 33
  • 48
  • Unfortunately, I don't get how would you like to use synchronization. Could you clarify it a bit? The main problem I have is not how to create a singleton, but how to create a pseudo-singleton which could be constructed with parameters. Or, alternatively, what solution can be used to pass one instance of XMPPConnection object between all WS calls. If you talk about many concurrent sessions - I want to create 1 connection object, which would be shared across all WS requests to send messages. The WS should return "success"/"failed" to the client. – Maciej Papież Sep 29 '11 at 09:18
  • See edit. Are you saying that the XMPP client itself is thread-safe? – ThomasRS Sep 29 '11 at 13:28
  • 1) Now I got your idea, but how would you pull the login info to the wrapping class? The only way I see is to init() it after construction and after checking if not initialized yet. It seems to me very similar as my solution from the question, isn't it? I'm a newbie, maybe there are some major differences that I simply don't notice:) 2) Well, I haven't thought about considering if the XMPP client is thread-safe, but it seems that [it is](http://community.igniterealtime.org/thread/19605). – Maciej Papież Sep 29 '11 at 14:34
  • The above is a lazy intialization, i.e. nothing will happen before someone accesses the getHelper() method. You can do all sorts of inits in the Helper constructor. And yes, there are subtile differences related to multithreading, but very logically similar to your initial suggestion :) – ThomasRS Sep 30 '11 at 14:45
0

It sounds to me like the wrong approach altogether. Why not simply have the WS requests put in a queue and have a single thread read the requests and write the messages via xmpp. There would only be a single connection, but there is no need to create any custom code to create a singleton connection at all.

Robin
  • 24,062
  • 5
  • 49
  • 58
  • I need the feedback if sending the message was successful, putting everything in a queue makes this impossible. Unfortunately. – Maciej Papież Sep 29 '11 at 22:30
  • No, you just need to use some sort of mechanism that will block and return the result, like a FutureTask. The main point is to simply share the single connection. – Robin Sep 30 '11 at 13:32