/*
 * @(#)ObjID.java	1.26 01/12/03
 *
 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package java.rmi.server;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.OutputStream;
import java.security.SecureRandom;
import java.util.Random;

/**
 * An <code>ObjID</code> is used to identify a remote object exported
 * to an RMI runtime.  When a remote object is exported, it is assigned
 * an object identifier either implicitly or explicitly, depending on
 * the API used to export.
 *
 * <p>The {@link #ObjID()} constructor can be used to generate a unique
 * object identifier.  Such an <code>ObjID</code> is unique over time
 * with respect to the host it is generated on.
 *
 * The {@link #ObjID(int)} constructor can be used to create a
 * "well-known" object identifier.  The scope of a well-known
 * <code>ObjID</code> depends on the RMI runtime it is exported to.
 *
 * <p>An <code>ObjID</code> instance contains an object number (of type
 * <code>long</code>) and an address space identifier (of type
 * {@link UID}).  In a unique <code>ObjID</code>, the address space
 * identifier is unique with respect to a given host over time.  In a
 * well-known <code>ObjID</code>, the address space identifier is
 * equivalent to one returned by invoking the {@link UID#UID(short)}
 * constructor with the value zero.
 *
 * <p>If the system property <code>java.rmi.server.randomIDs</code>
 * is defined to equal the string <code>"true"</code> (case insensitive),
 * then the {@link #ObjID()} constructor will use a cryptographically
 * strong random number generator to choose the object number of the
 * returned <code>ObjID</code>.
 *
 * @author	Ann Wollrath
 * @author	Peter Jones
 * @version	1.26, 01/12/03
 * @since	JDK1.1
 */
public final class ObjID implements java.io.Serializable {

    /** object number for well-known <code>ObjID</code> of the registry */
    public static final int REGISTRY_ID = 0;

    /** object number for well-known <code>ObjID</code> of the activator */
    public static final int ACTIVATOR_ID = 1;

    /**
     * object number for well-known <code>ObjID</code> of
     * the distributed garbage collector
     */
    public static final int DGC_ID = 2;

    /** indicate compatibility with JDK 1.1.x version of class */
    private static final long serialVersionUID = -6386392263968365220L;

    private static final UID mySpace;
    private static final Random generator;

    /**
     * @serial object number
     * @see #hashCode
     */
    private final long objNum;

    /**
     * @serial address space identifier (unique to host over time)
     */
    private final UID space;

    /**
     * Generates a unique object identifier.
     *
     * <p>If the system property <code>java.rmi.server.randomIDs</code>
     * is defined to equal the string <code>"true"</code> (case insensitive),
     * then this constructor will use a cryptographically
     * strong random number generator to choose the object number of the
     * returned <code>ObjID</code>.
     */
    public ObjID() {
	/*
	 * Create a new UID if the SecureRandom generator is used (mySpace
	 * will be null in this case).  Using a different UID for each ObjID
	 * ensures that ObjIDs will be unique in this VM incarnation when
	 * paired with the result of the secure random number generator.
	 */
	space = (mySpace != null) ? mySpace : new UID();
	objNum = generator.nextLong();
    }

    /**
     * Creates a "well-known" object identifier.
     *
     * <p>An <code>ObjID</code> created via this constructor will not
     * clash with any <code>ObjID</code>s generated via the no-arg
     * constructor.
     *
     * @param	objNum object number for well-known object identifier
     */
    public ObjID(int objNum) {
	space = new UID((short) 0);
	this.objNum = objNum;
    }

    /**
     * Constructs an object identifier given data read from a stream.
     */
    private ObjID(long objNum, UID space) {
	this.objNum = objNum;
	this.space = space;
    }

    /**
     * Marshals a binary representation of this <code>ObjID</code> to
     * an <code>ObjectOutput</code> instance.
     *
     * <p>Specifically, this method first invokes the given stream's
     * {@link ObjectOutput#writeLong(long)} method with this object
     * identifier's object number, and then it writes its address
     * space identifier by invoking its {@link UID#write(DataOutput)}
     * method with the stream.
     *
     * @param	out the <code>ObjectOutput</code> instance to write
     * this <code>ObjID</code> to
     *
     * @throws	IOException if an I/O error occurs while performing
     * this operation
     */
    public void write(ObjectOutput out) throws IOException {
	out.writeLong(objNum);
	space.write(out);
    }

    /**
     * Constructs and returns a new <code>ObjID</code> instance by
     * unmarshalling a binary representation from an
     * <code>ObjectInput</code> instance.
     *
     * <p>Specifically, this method first invokes the given stream's
     * {@link ObjectInput#readLong()} method to read an object number,
     * then it invokes {@link UID#read(DataInput)} with the
     * stream to read an address space identifier, and then it
     * creates and returns a new <code>ObjID</code> instance that
     * contains the object number and address space identifier that
     * were read from the stream.
     *
     * @param	in the <code>ObjectInput</code> instance to read
     * <code>ObjID</code> from
     *
     * @return	unmarshalled <code>ObjID</code> instance
     *
     * @throws	IOException if an I/O error occurs while performing
     * this operation
     */
    public static ObjID read(ObjectInput in) throws IOException {
	long num = in.readLong();
	UID space = UID.read(in);
	return new ObjID(num, space);
    }

    /**
     * Returns the hash code value for this object identifier, the
     * object number.
     *
     * @return	the hash code value for this object identifier
     */
    public int hashCode() {
	return (int) objNum;
    }

    /**
     * Compares the specified object with this <code>ObjID</code> for
     * equality.
     *
     * This method returns <code>true</code> if and only if the
     * specified object is an <code>ObjID</code> instance with the same
     * object number and address space identifier as this one.
     *
     * @param	obj the object to compare this <code>ObjID</code> to
     *
     * @return	<code>true</code> if the given object is equivalent to
     * this one, and <code>false</code> otherwise
     */
    public boolean equals(Object obj) {
	if (obj instanceof ObjID) {
	    ObjID id = (ObjID)obj;
	    return objNum == id.objNum && space.equals(id.space);
	} else {
	    return false;
	}
    }

    /**
     * Returns a string representation of this object identifier.
     *
     * @return	a string representation of this object identifier
     */
    /*
     * The address space identifier is only included in the string
     * representation if it does not denote the local address space
     * (or if the randomIDs property was set).
     */
    public String toString() {
	return "[" + (space.equals(mySpace) ? "" : space + ", ") +
	    objNum + "]";
    }

    private static final class InsecureRandom extends Random {

	/** unnecessary serialVersionUID to keep watchdog tools happy */
	private static final long serialVersionUID = -698228687531590145L;

	private long nextNum;

	public synchronized long nextLong() {
	    return nextNum++;
	}
    }

    static {
	boolean randomIDs =
	    ((Boolean) java.security.AccessController.doPrivileged(
	        new sun.security.action.GetBooleanAction(
		    "java.rmi.server.randomIDs"))).booleanValue();

	if (randomIDs) {
	    generator = new SecureRandom();
	    mySpace = null;
	} else {
	    generator = new InsecureRandom();
	    /*
	     * The InsecureRandom implementation guarantees that object
	     * numbers will not repeat, so the same UID value can be used
	     * for all instances of ObjID.
	     */
	    mySpace = new UID();
	}
    }
}
