powered by Jive Software

Custom ServiceDiscoveryManager

Hi,

I’d like to create a custom ServiceDiscoveryManger, but I’m running into a problem described here:

http://www.igniterealtime.org/community/message/128468#128468

Let me explain why I want a custom manager:

We want to use disco#items for a client to advertise a complex hierarchy of nodes, possibly hundreds in all. In this situation, it’s impractical to use setNodeInformationProvider( node, provider ) to set the information for every node ahead of time. Instead, it’s much more sane to ‘look up’ the node info (or at least the child items) when the request comes in for each node.

Unfortunately, that static initializer block means that I can’t practically subclass ServiceDiscoveryManger, because simply loading that class tells ServiceDiscoveryManager to essentially ‘do its thing’ with a base implementation instance and even if I create a subclass I’ll have two packet listeners contending over the same messages. Furthermore the ConnectionListener and PacketListeners that are created by ServiceDiscovyeryManger are anonymous subclasses, so I can’t remove them once they’ve been added by ServiceDiscoveryManger either!

As far as I can tell, the only way I can avoid this is by removing ServiceDiscoveryManager from smack-config.xml, and hoping that no other piece of code references that class, so that it never gets loaded and attached to my connection(s). Amirite?

Dear Ignite developers,

I’d be more than happy to do the legwork to fix this but I’d like an inkling of affirmation before I go through the effort so that I know I’m not wasting my time.

The solution could be relatively straightforward, simple as moving that static init code into a separate factory class or something similar. For example:

public interface ExtensionFactory {
     void initialize();
} public class ServiceDiscoveryManagerFactory implements ExtensionFactory {
     // Now we can reference this instance if we ever need to remove it!!!
     public static final discoConnectionListener = new ConnectionCreationListener() {
           public void connectionCreated(Connection connection) {
                new ServiceDiscoveryManager(connection);
           }
        };
     public void initilize() {
        Connection.addConnectionCreationListener( discoConnectionListener );
     }
}

Easy, right? Now the extension initializer code that reads smack-config.xml has to do a simple extra step in order to run the initializer… Nothing special, it just has to call a method rather than simply reference the class

// in /SmackConfiguration.java :
     private static void parseClassToLoad(XmlPullParser parser) throws Exception {
         String className = parser.nextText();
         // Attempt to load the class so that the class can get initialized
         try {
             Class clazz = Class.forName(className);
             /* we'll do a check since conceivably other users could be loading extensions                 via the old static-initializer routine, in which case they'd be done right                 now.  So we'll go an extra step of checking and running the init method                 only the class it implements our new mechanism: */
             if ( ExtensionFactory.class.isAssignableFrom( clazz ) {
                 ExtensionFactory initializer = (ExtensionFactory) clazz.newInstance();
                 initializer.initialize(); // done!
             }
        }
        catch (ClassNotFoundException cnfe) {
             System.err.println("Error! A startup class specified in smack-config.xml could " +
                     "not be loaded: " + className);
        }
    }

And voila! I can extend ServiceDiscoveryManger without it latching itself on to every XMPPConnection like a bloody leech I’ll submit a patch once I hear from a dev that you think this is an OK idea.