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

import org.jboss.remoting.network.NetworkRegistry;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.InetAddress;
import java.rmi.dgc.VMID;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.WeakHashMap;

/**
 * Identity is used to uniquely identify a JBoss server instance on the network.
 *
 * @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
 * @version $Revision: 1.8.10.3 $
 */
public class Identity implements Serializable
{
    static final long serialVersionUID = -2788084303665751253L;

    private static transient Random random = new Random(System.currentTimeMillis());
    public static transient String DEFAULT_DOMAIN = "JBOSS";
    private static transient String _domain = System.getProperty("jboss.identity.domain",DEFAULT_DOMAIN);
    private static transient Map identities = new WeakHashMap(2);

    private final String instanceid;
    private final InetAddress ip;
    private final String serverid;
    private String domain;
    private int hashCode;

    private Identity(InetAddress addr, String instanceid, String serverid)
    {
        this.ip = addr;
        this.instanceid = instanceid;
        this.serverid = serverid;
        this.domain = ((_domain==null||_domain.equals("")) ? DEFAULT_DOMAIN : _domain);
        calcHashCode();
    }
    private void calcHashCode ()
    {
        this.hashCode = (ip.hashCode() + instanceid.hashCode() + serverid.hashCode() - domain.hashCode());
    }

    /**
     * set the domain for all active mbean servers
     */
    public static void setDomain (String domain)
    {
        Iterator iter = identities.keySet().iterator ();
        while (iter.hasNext ())
        {
            Identity ident = (Identity)identities.get(iter.next());
            if (ident!=null)
            ident.domain=domain;
            ident.calcHashCode();
        }
        System.setProperty("jboss.identity.domain",domain);
        _domain = domain;
        NetworkRegistry.getInstance().changeDomain(domain);
    }


    public int hashCode()
    {
        return hashCode;
    }


    public String toString()
    {
        return "JBOSS Identity [address:" + ip + ",instanceid:" + instanceid + ",JMX id:" + serverid + ",domain:" + domain + "]";
    }

    /**
     * return the domain for the server
     *
     * @return
     */
    public final String getDomain()
    {
        return domain;
    }

    /**
     * return the JBOSS Instance ID, which is the same between reboots of the same
     * JBOSS server instance.
     *
     * @return
     */
    public String getInstanceId()
    {
        return this.instanceid;
    }

    /**
     * return the JBOSS IP Address of the instance
     *
     * @return
     */
    public InetAddress getAddress()
    {
        return this.ip;
    }

    /**
     * return the JMX server ID for the JBoss instance, which is different between
     * reboots of the same JBOSS server instance.
     *
     * @return
     */
    public String getJMXId()
    {
        return this.serverid;
    }

    /**
     * returns true if the identity represents the same JVM
     *
     * @param identity
     * @return
     */
    public boolean isSameJVM(Identity identity)
    {
        return identity.equals(this);
    }

    /**
     * returns true if the identity represents the same JBOSS Instance, although
     * may not be the exact same process space since the JVM process space might be
     * different (such as a reboot).
     *
     * @param identity
     * @return
     */
    public boolean isSameInstance(Identity identity)
    {
        return identity.getInstanceId().equals(instanceid);
    }

    /**
     * returns true if the identity is on the same JBOSS machine, represented by the
     * same IP address (this may not work in the case several physically different
     * machines have the same IP Address).
     *
     * @param identity
     * @return
     */
    public boolean isSameMachine(Identity identity)
    {
        return identity.getAddress().equals(ip);
    }

    public boolean equals(Object obj)
    {
        if(obj instanceof Identity)
        {
            return hashCode == obj.hashCode();
        }
        return false;
    }

    public static synchronized final Identity get(MBeanServer server)
    {
        if(identities.containsKey(server))
        {
            return (Identity) identities.get(server);
        }
        try
        {
            String serverid = (String)server.getAttribute(new ObjectName("JMImplementation:type=MBeanServerDelegate"), "MBeanServerId");
            Identity identity = new Identity(InetAddress.getLocalHost(),createId(server),serverid);
            identities.put(server,identity);
            return identity;
        }
        catch(Exception ex)
        {
            throw new RuntimeException("Exception creating identity: " + ex.getMessage());
        }
    }

    private static final synchronized String createId (MBeanServer server)
    {
        // we can set as a system property
        String myid = System.getProperty("jboss.identity");
        if(myid != null)
        {
            return myid;
        }
        String id = null;
        File file = null;
        try
        {
            // FIRST TRY THE JBOSS guy to determine our data directory
            ObjectName obj = new ObjectName("jboss.system:type=ServerConfig");
            File dir = (File) server.getAttribute(obj, "ServerDataDir");
            if(dir != null)
            {
                if(dir.exists() == false)
                {
                    dir.mkdirs();
                }
                file = new File(dir, "jboss.identity");
            }
        }
        catch(Exception ex)
        {
        }
        if(file == null)
        {
            // we may not have that mbean, which is OK
            String fl = System.getProperty("jboss.identity.dir", ".");
            File dir = new File(fl);
            if(dir.exists() == false)
            {
                dir.mkdirs();
            }
            file = new File(dir, "jboss.identity");
        }
        if(file.exists() && file.canRead())
        {
            InputStream is = null;
            try
            {
                is = new FileInputStream(file);
                byte buf[] = new byte[800];
                int c = is.read(buf);
                id = new String(buf, 0, c);
            }
            catch(Exception ex)
            {
                throw new RuntimeException("Error loading jboss.identity: " + ex.toString());
            }
            finally
            {
                if(is != null)
                {
                    try
                    {
                        is.close();
                    }
                    catch(Exception ig)
                    {
                    }
                }
            }
        }
        else
        {
            OutputStream out = null;
            try
            {
                id = createUniqueID();
                if(file.exists() == false)
                {
                    file.createNewFile();
                }
                out = new FileOutputStream(file);
                out.write(id.getBytes());
            }
            catch(Exception ex)
            {
                throw new RuntimeException("Error creating Instance ID: " + ex.toString());
            }
            finally
            {
                if(out != null)
                {
                    try
                    {
                        out.flush();
                        out.close();
                    }
                    catch(Exception ig)
                    {
                    }
                }
            }
        }
        System.setProperty("jboss.identity", id);
        return id;
    }

    public static final String createUniqueID()
    {
        String id = new VMID().toString();
        // colons don't work in JMX
        return id.replace(':', 'x') + random.nextInt(1000);
    }
}