/***************************************
 *                                     *
 *  JBoss: The OpenSource J2EE WebOS   *
 *                                     *
 *  Distributable under LGPL license.  *
 *  See terms of license at gnu.org.   *
 *                                     *
 ***************************************/
package org.jboss.mx.remoting;

import java.util.StringTokenizer;

import javax.management.MBeanServer;

import org.jboss.logging.Logger;
import org.jboss.remoting.InvokerLocator;
import org.jboss.remoting.ConnectionFailedException;
import org.jboss.remoting.ident.Identity;

/**
 * MBeanTransportPreference is a utility class that will take into account the VM's setup transport
 * preferences when trying to create a preferred connection back to a remote MBeanServer.  Since there
 * are cases when multiple invoker transports can exist and all be valid, this will help determine which
 * order the transports should be attempted.  You wouldn't want to connect via SOAP, in most cases, when
 * TCP/IP via Sockets is available.  There are cases however you do want to explicitly choose SOAP vs.
 * Sockets or RMI, and in which case your preference order might be <tt>soap, socket, rmi.</tt>
 *
 * @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
 * @version $Revision: 1.2 $
 */
public class MBeanTransportPreference
{
    private static final transient Logger log = Logger.getLogger(MBeanTransportPreference.class.getName());

    // NOTE: we need to maybe think this through on how this really should work long term..
    private static String _preferences = System.getProperty("jboss.transport.preferences","socket,rmi,soap");
    private static String preferences[] = initialize(_preferences);
    private static MBeanServer ourServer;
    private static Identity ourIdentity;

    public static void setLocalServer (MBeanServer server, Identity identity)
    {
        if (log.isDebugEnabled())
        {
            log.debug("setLocalServer called - server="+server+",identity="+identity);
        }
        ourServer = server;
        ourIdentity = identity;
    }

    private static String[] initialize (String list)
    {
        if (list==null)
        {
            return new String[1];
        }
        StringTokenizer tok=new StringTokenizer(list,",");
        String pref [] = new String[tok.countTokens()];
        int c=0;
        while(tok.hasMoreTokens())
        {
            String token=tok.nextToken();
            pref[c++]=token.trim();
        }
        return pref;
    }
    /**
     * set the order to use when selecting transports to connect to a remote server
     *
     * @param order
     */
    public static void setTransportPreferences (String order[])
    {
        preferences = (order==null || order.length<=0) ? initialize(_preferences) : order;
    }
    /**
     * get the order to use when selecting transports to connect to a remote server
     *
     * @return
     */
    public static String[] getTransportPreferences ()
    {
        return preferences;
    }
    /**
     * return a server transport to a MBeanServer on a remote server, using the transport
     * preference order specified by the user
     *
     * @param identity
     * @param locators
     * @return
     * @throws ConnectionFailedException
     */
    public static MBeanServer getServerByTransport (Identity identity, InvokerLocator locators[])
        throws ConnectionFailedException
    {
        if (log.isDebugEnabled())
        {
            log.debug("getServerByTransport for identity="+identity+", ours is="+ourIdentity);
        }
        if (ourIdentity==null)
        {
            if (ourServer==null)
            {
                 ourServer = JMXUtil.getMBeanServer();
            }
            ourIdentity = Identity.get(ourServer);
        }
        if (identity.isSameJVM(ourIdentity))
        {
            return ourServer;
        }
        for (int c=0;c<preferences.length;c++)
        {
            String transport = preferences[c];

            if (transport!=null)
            {
                for (int x=0;x<locators.length;x++)
                {
                    if (locators[x].getProtocol().equals(transport))
                    {
                        // attempt connect to this first one in our pref list
                        try
                        {
                            MBeanServer svr = MBeanServerRegistry.getMBeanServerFor(locators[x]);
                            if (svr!=null)
                            {
                                return svr;
                            }
                            svr = MBeanServerClientInvokerProxy.create(locators[x],ourIdentity.getJMXId(),identity.getJMXId());
                            if (svr!=null)
                            {
                                return svr;
                            }
                        }
                        catch (Throwable ex) {}
                    }
                }
            }
        }
        for (int x=0;x<locators.length;x++)
        {
            // attempt connect to this first one in our pref list
            try
            {
                if (log.isDebugEnabled())
                {
                   log.debug("attempting to connect via locator["+x+"] ("+locators[x]+") to: "+identity);
                }
                MBeanServer svr = MBeanServerRegistry.getMBeanServerFor(locators[x]);
                if (svr!=null)
                {
                    return svr;
                }
                svr = MBeanServerClientInvokerProxy.create(locators[x],ourIdentity.getJMXId(),identity.getJMXId());
                if (svr!=null)
                {
                    return svr;
                }
            }
            catch (Throwable ex)
            {
                if (log.isDebugEnabled())
                {
                    log.debug("Error connecting ... ",ex);
                }
            }
        }
        throw new ConnectionFailedException ("No transport/connection available to connect to: "+identity);
    }
}