/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.remoting;

import org.jboss.remoting.transport.ClientInvoker;

import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import java.net.InetAddress;

/**
 * InvokerLocator is an object that indentifies a specific Invoker on the network, via a unique
 * locator URI. The locator URI is in the format: <P>
 *
 *            <tt>protocol://host[:port][/path[?param=value&param2=value2]]</tt>     <P>
 *
 * For example, a SOAP based locator might be: <P>
 *
 *            <tt>soap://192.168.10.1:8081/axis/invoker</tt>  <P>
 *
 * An example Socket based locator might be: <P>
 *
 *            <tt>socket://192.168.10.1:9999</tt>  <P>
 *
 * An example RMI based locator might be: <P>
 *
 *            <tt>rmi://localhost</tt>  <P>
 *
 * NOTE: the hostname will automatically be resolved to the outside IP address of the local machine
 * if <tt>localhost</tt> or <tt>127.0.0.1</tt> is used as the hostname in the URI.  If it cannot be
 * determined or resolved, it will use what was passed.
 *
 *
 * @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
 * @author <a href="mailto:telrod@e2technologies.net">Tom Elrod</a>
 * @version $Revision: 1.6.2.1 $
 */
public class InvokerLocator implements Serializable
{
	private static final long serialVersionUID = -2909329895029296248L;
    protected String protocol;
    protected String host;
    protected int port;
    protected String path;
    protected Map parameters;
    private String uri;

    public InvokerLocator (String uri)
        throws MalformedURLException
    {
        int i = uri.indexOf("://");
        if (i<0)
        {
            throw new MalformedURLException();
        }
        String tmp = uri.substring(i+3);
        this.protocol = uri.substring(0,i);
        i = tmp.indexOf("/");
        int p = tmp.indexOf(":");
        if (p!=-1)
        {
            host = resolveHost(tmp.substring(0,p).trim());
            if (i>-1)
            {
                port = Integer.parseInt(tmp.substring(p+1,i));
            }
            else
            {
                port = Integer.parseInt(tmp.substring(p+1));
            }
        }
        else
        {
            if (i>-1)
            {
                host = resolveHost(tmp.substring(0,i).trim());
            }
            else
            {
                host = resolveHost(tmp.substring(0).trim());
            }
            port = -1;
        }
        p = tmp.indexOf("?");
        if (p!=-1)
        {
            path = tmp.substring(i+1,p);
            String args = tmp.substring(p+1);
            StringTokenizer tok=new StringTokenizer(args,"&");
            parameters=new HashMap(tok.countTokens());
            while(tok.hasMoreTokens())
            {
                String token=tok.nextToken();
                int eq = token.indexOf("=");
                String name = (eq>-1)?token.substring(0,eq) : token;
                String value = (eq>-1)?token.substring(eq+1) : "";
                parameters.put(name,value);
            }
        }
        else
        {
            path = "";
        }
		// rebuild it, since the host probably got resolved and the port changed
        this.uri = protocol+"://"+this.host+((port>-1)?(":"+port):"")+"/"+path+((parameters!=null)?"?":"");
        if (parameters!=null)
        {
            Iterator iter=parameters.keySet().iterator();
            while (iter.hasNext())
            {
                String key=(String)iter.next();
                String val=(String)parameters.get(key);
                this.uri+=key+"="+val;
                if (iter.hasNext())
                {
                    this.uri+="&";
                }
            }
        }
    }
    /**
     * since a user will usually say <tt>localhost</tt> or <tt>127.0.0.1</tt>, we
     * need to resolve that to a real external IP address that another machine can
     * connect to
     */
    private static final String resolveHost (String host)
    {
		if (host.equals("localhost") || host.equals("127.0.0.1"))
		{
			try
			{
				return InetAddress.getLocalHost().getHostAddress();
			}
			catch (Exception ex)
			{
				return host;
			}
		}
		try
		{
			return InetAddress.getByName(host).getHostAddress();
		}
		catch (Exception ex)
		{
			return host;
		}
	}

    public InvokerLocator (String protocol, String host, int port, String path, Map parameters)
    {
        this.protocol = protocol;
        this.host = resolveHost(host);
        this.port = port;
        this.path = path;
        this.parameters = parameters;

        this.uri = protocol+"://"+this.host+((port>-1)?(":"+port):"")+"/"+path+((parameters!=null)?"?":"");
        if (parameters!=null)
        {
            Iterator iter=parameters.keySet().iterator();
            while (iter.hasNext())
            {
                String key=(String)iter.next();
                String val=(String)parameters.get(key);
                this.uri+=key+"="+val;
                if (iter.hasNext())
                {
                    this.uri+="&";
                }
            }
        }
    }
    public int hashCode ()
    {
        return uri.hashCode();
	}

	public boolean equals (Object obj)
	{
		return obj instanceof InvokerLocator && obj.hashCode()==hashCode();
	}
    /**
     * return the locator URI, in the format: <P>
     *
     * <tt>protocol://host[:port][/path[?param=value&param2=value2]]</tt>
     *
     * @return
     */
    public String getLocatorURI ()
    {
        return uri;
    }
    public String getProtocol ()
    {
        return protocol;
    }
    public String getHost ()
    {
        return host;
    }
    public int getPort ()
    {
        return port;
    }
    public String getPath ()
    {
        return path;
    }
    public Map getParameters ()
    {
        return parameters;
    }
    public String toString ()
    {
        return "InvokerLocator ["+uri+"]";
    }

    /**
     * narrow this invoker to a specific RemoteClientInvoker instance
     *
     * @return
     * @throws Exception
     */
    public ClientInvoker narrow () throws Exception
    {
        return InvokerRegistry.createClientInvoker(this);
    }

    /**
     * testing
     *
     * @param args
     */
    public static void main (String args[])
    {
        try
        {
           System.out.println("soap://localhost:8080/urn?test=test&test2=test2  ==>"+new InvokerLocator("soap://localhost:8080/urn?test=test&test2=test2"));
           System.out.println("soap://localhost  ==>"+new InvokerLocator("soap://localhost"));
           System.out.println("soap://localhost/urn?test=test&test2=test2 ==>"+new InvokerLocator("soap://localhost/urn?test=test&test2=test2"));
           System.out.println("soap://localhost:8080/urn ==>"+new InvokerLocator("soap://localhost:8080/urn"));
           System.out.println("soap://localhost/urn ==>"+new InvokerLocator("soap://localhost/urn"));
           System.out.println("soap://localhost:8080/?test1=test2 ==>"+new InvokerLocator("soap://localhost:8080/?test1=test2"));
           System.out.println("socket://127.0.0.1:2222 ==>"+new InvokerLocator("socket://127.0.0.1:2222"));
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }
}
