/* gnuAbstractPOA.java --
   Copyright (C) 2005 Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Classpath 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
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package gnu.CORBA.Poa;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;

import org.omg.CORBA.BAD_INV_ORDER;
import org.omg.CORBA.BAD_PARAM;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.LocalObject;
import org.omg.CORBA.NO_IMPLEMENT;
import org.omg.CORBA.OBJ_ADAPTER;
import org.omg.CORBA.ORB;
import org.omg.CORBA.Object;
import org.omg.CORBA.Policy;
import org.omg.CORBA.SetOverrideType;
import org.omg.CORBA.TRANSIENT;
import org.omg.CORBA.portable.ObjectImpl;
import org.omg.PortableInterceptor.NON_EXISTENT;
import org.omg.PortableInterceptor.ObjectReferenceFactory;
import org.omg.PortableInterceptor.ObjectReferenceTemplate;
import org.omg.PortableInterceptor.ObjectReferenceTemplateHelper;
import org.omg.PortableServer.AdapterActivator;
import org.omg.PortableServer.ForwardRequest;
import org.omg.PortableServer.IdAssignmentPolicy;
import org.omg.PortableServer.IdAssignmentPolicyValue;
import org.omg.PortableServer.IdUniquenessPolicy;
import org.omg.PortableServer.IdUniquenessPolicyValue;
import org.omg.PortableServer.ImplicitActivationPolicy;
import org.omg.PortableServer.ImplicitActivationPolicyValue;
import org.omg.PortableServer.LifespanPolicy;
import org.omg.PortableServer.LifespanPolicyValue;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAManager;
import org.omg.PortableServer.RequestProcessingPolicy;
import org.omg.PortableServer.RequestProcessingPolicyValue;
import org.omg.PortableServer.SERVANT_RETENTION_POLICY_ID;
import org.omg.PortableServer.Servant;
import org.omg.PortableServer.ServantActivator;
import org.omg.PortableServer.ServantLocator;
import org.omg.PortableServer.ServantManager;
import org.omg.PortableServer.ServantRetentionPolicy;
import org.omg.PortableServer.ServantRetentionPolicyValue;
import org.omg.PortableServer.ThreadPolicy;
import org.omg.PortableServer.ThreadPolicyValue;
import org.omg.PortableServer.POAManagerPackage.State;
import org.omg.PortableServer.POAPackage.AdapterAlreadyExists;
import org.omg.PortableServer.POAPackage.AdapterNonExistent;
import org.omg.PortableServer.POAPackage.InvalidPolicy;
import org.omg.PortableServer.POAPackage.NoServant;
import org.omg.PortableServer.POAPackage.ObjectAlreadyActive;
import org.omg.PortableServer.POAPackage.ObjectNotActive;
import org.omg.PortableServer.POAPackage.ServantAlreadyActive;
import org.omg.PortableServer.POAPackage.ServantNotActive;
import org.omg.PortableServer.POAPackage.WrongAdapter;
import org.omg.PortableServer.POAPackage.WrongPolicy;

import gnu.CORBA.OrbFunctional;
import gnu.CORBA.CDR.BufferredCdrInput;
import gnu.CORBA.CDR.BufferedCdrOutput;

/**
 * Our POA implementation.
 *
 * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
 */
public class gnuPOA
  extends LocalObject
  implements POA, ObjectReferenceFactory
{
  /**
   * The object reference template, associated with this POA.
   * 
   * @since 1.5
   */
  class RefTemplate implements ObjectReferenceTemplate
  {
    /** 
     * Use serialVersionUID for interoperability. 
     */
    private static final long serialVersionUID = 1;
    
    RefTemplate()
    {
      // The adapter name is computed once.
      ArrayList names = new ArrayList();
      names.add(the_name());

      POA poa = the_parent();
      while (poa != null)
        {
          names.add(poa.the_name());
          poa = poa.the_parent();
        }

      // Fill in the string array in reverse (more natural) order,
      // root POA first.
      m_adapter_name = new String[names.size()];

      for (int i = 0; i < m_adapter_name.length; i++)
        m_adapter_name[i] = (String) names.get(m_adapter_name.length - i - 1);
    }
    
    /**
     * The adapter name
     */
    final String[] m_adapter_name;
    
    /**
     * Get the name of this POA.
     */
    public String[] adapter_name()
    {
      return (String[]) m_adapter_name.clone();
    }

    /**
     * Get the ORB id.
     */
    public String orb_id()
    {
      return m_orb.orb_id;
    }

    /**
     * Get the server id.
     */
    public String server_id()
    {
      return OrbFunctional.server_id;
    }

    /**
     * Create the object.
     */
    public Object make_object(String repositoryId, byte[] objectId)
    {
      return create_reference_with_id(objectId, repositoryId);
    }

    /**
     * Get the array of truncatible repository ids.
     */
    public String[] _truncatable_ids()
    {
      return ref_template_ids;
    }
  }
  
  /** 
   * Use serialVersionUID for interoperability. 
   */
  private static final long serialVersionUID = 1;
  
  /**
   * The adapter reference template.
   */
  ObjectReferenceTemplate refTemplate;
  
  /**
   * The reference template repository ids. Defined outside the class as it
   * cannot have static members.
   */
  final static String[] ref_template_ids = 
    new String[] { ObjectReferenceTemplateHelper.id() };
  
  /**
   * The active object map, mapping between object keys, objects and servants.
   */
  public final AOM aom = new AOM();

  /**
   * The children of this POA.
   */
  final ArrayList children = new ArrayList();

  /**
   * The name of this POA.
   */
  final String name;

  /**
   * The parent of this POA (null for the root POA).
   */
  final POA parent;

  /**
   * The ior key signature, indicating, that the ior key is encoded using
   * internal agreements of this implementation (0x'free').
   */
  static final int SIGNATURE = 0x66726565;

  /**
   * The adapter activator for this POA, null if no activator is set.
   */
  AdapterActivator m_activator;

  /**
   * The POA manager for this POA.
   */
  POAManager m_manager;

  /**
   * The servant manager (servant activator) for this POA.
   */
  ServantActivator servant_activator;

  /**
   * The servant manager (servant locator) for this POA.
   */
  ServantLocator servant_locator;

  /**
   * The default servant, if on is in use.
   */
  Servant default_servant;

  /**
   * The cached poa id value, computed once.
   */
  private byte[] m_poa_id;

  /**
   * The all policy values that apply to this POA.
   * The used policy values are singletons, unique between policies.
   */
  private final HashSet m_policies;

  /**
   * An array of the set policies.
   */
  Policy[] s_policies;

  /**
   * The ORB, where the POA is connected.
   */
  final ORB_1_4 m_orb;

  /**
   * When true, the POA is being destroyed or is destroyed.
   */
  boolean m_inDestruction;

  /**
   * True if the active object map is used by this POA.
   * The value is moved into separate boolean value due
   * necessity of the frequent checks.
   */
  public final boolean retain_servant;
  
  /**
   * The object reference factory, used to create the new object
   * references.
   */
  ObjectReferenceFactory m_object_factory = this;

  /**
   * Create a new abstract POA.
   *
   * @param a_parent the parent of this POA.
   * @param a_name a name for this POA.
   * @param a_manager a manager for this POA. If null, a new
   * {@link gnuPOAManager} will be instantiated.
   * @param a_policies an array of policies that apply to this POA.
   * @param an_orb an ORB for this POA.
   */
  public gnuPOA(gnuPOA a_parent, String a_name, POAManager a_manager,
                Policy[] a_policies, ORB_1_4 an_orb
               )
         throws InvalidPolicy
  {
    // Add default policies.
    Policy[] all_policies = StandardPolicies.withDefault(a_policies);

    name = a_name;
    parent = a_parent;
    m_orb = an_orb;

    if (a_manager != null)
      m_manager = a_manager;
    else
      m_manager = new gnuPOAManager();

    if (m_manager instanceof gnuPOAManager)
      {
        gnuPOAManager g = (gnuPOAManager) m_manager;
        g.addPoa(this);
      }

    m_policies = new HashSet(all_policies.length);

    s_policies = new Policy[ all_policies.length ];
    for (int i = 0; i < s_policies.length; i++)
      {
        s_policies [ i ] = all_policies [ i ].copy();
        m_policies.add(((AccessiblePolicy) s_policies [ i ]).getValue());
      }

    retain_servant = applies(ServantRetentionPolicyValue.RETAIN);

    validatePolicies(a_policies);
    
    refTemplate = new RefTemplate();
  }

  /**
   * Wait while at least one of the threads in this POA is actively
   * processing one of requests.
   */
  public void waitWhileRunning()
  {
    // First pause.
    long time = 1;

    // Maximal duration between checks.
    long max = 500;

    boolean runs;

    do
      {
        runs = m_orb.currents.has(this);

        if (runs)
          {
            // Avoid taking CPU resources
            // from the thread that is running.
            try
              {
                Thread.sleep(time);
                time = time * 2;
                if (time > max)
                  time = max;
              }
            catch (InterruptedException ex)
              {
              }
          }
      }
    while (runs);
  }

  /**
   * Etherealize all objects, associated with this POA. Invoked from the
   * {@link gnuPOAManager} only if it is known that the servant_activator
   * holds non-null value.
   */
  protected void etherealizeAll()
  {
    if (servant_activator == null)
      return;

    ArrayList keys = new ArrayList();
    keys.addAll(aom.keySet());

    byte[] key;
    AOM.Obj obj;
    boolean last;
    for (int i = 0; i < keys.size(); i++)
      {
        key = (byte[]) keys.get(i);
        obj = aom.get(key);

        if (obj.poa == this)
          {
            aom.remove(key);

            if (!obj.isDeactiveted())
              {
                // Check if the servant still stays under the other key.
                last = aom.findServant(obj.servant) == null;
                servant_activator.etherealize(obj.key, this, obj.servant, true,
                                              last
                                             );
              }
          }
      }
  }

  /**
   * Create an instance of the POA with the given features.
   * This method is not responsible for duplicate checking
   * or adding the returned instance to any possible table.
   *
   * @param child_name the name of the poa being created.
   * @param manager the poa manager (never null).
   * @param policies the array of policies.
   * @param an_orb the ORB for this POA.
   *
   * @return the created POA.
   *
   * @throws InvalidPolicy for conflicting or otherwise invalid policies.|
   */
  protected POA createPoaInstance(String child_name, POAManager a_manager,
                                  Policy[] policies, ORB_1_4 an_orb
                                 )
                           throws InvalidPolicy
  {
    POAManager some_manager =
      a_manager == null ? new gnuPOAManager() : a_manager;

    if (some_manager instanceof gnuPOAManager)
      {
        ((gnuPOAManager) some_manager).addPoa(this);
      }

    return new gnuPOA(this, child_name, some_manager, policies, an_orb);
  }

  /**
   * Check if the given policy value applies to this POA.
   *
   * @param policy_value a policy value to check. The policy values are
   * singletons and unique between the different policies, so the policy
   * type is not passed.
   *
   * @return true if the policy value applies, false otherwise.
   */
  public final boolean applies(java.lang.Object policy_value)
  {
    return m_policies.contains(policy_value);
  }

  /**
   * Check for the presence of the required policy.
   *
   * @param policy_value a policy value to check.
   *
   * @throws WrongPolicy if the required policy value is not applicable.
   */
  public final void required(java.lang.Object policy_value)
                      throws WrongPolicy
  {
    if (!applies(policy_value))
      throw new WrongPolicy(policy_value + " policy required.");
  }

  /**
   * Check for the absence of the given policy.
   *
   * @param policy_value a policy value to check.
   *
   * @throws WrongPolicy if the passed policy value is applicable.
   */
  public final void excluding(java.lang.Object policy_value)
                       throws WrongPolicy
  {
    if (applies(policy_value))
      throw new WrongPolicy(policy_value + " policy applies.");
  }

  /**
  * Find and optionally activate the child POA with the given name.
  *
  * @param poa_name the name of the POA to find.
  * @param activate_it if the child with the specified name is not found
  * or inactive and this parameter is true, the target POA activator is
  * invoked to activate that child. If this succeeds, that child POA
  * is returned.
  *
  * @throws AdapterNonExistent if no active child with the given name
  * is found and one of the following is true:
  * a) the target POA has no associated
  * {@link AdapterActivator}. b) that activator fails to activate the
  * child POA. c) <code>activate_id</code> = false.
  */
  public POA find_POA(String poa_name, boolean activate_it)
               throws AdapterNonExistent
  {
    POA child;
    for (int i = 0; i < children.size(); i++)
      {
        child = (POA) children.get(i);
        if (child.the_name().equals(poa_name))
          return child;
      }

    if (activate_it && m_activator != null)
      {
        boolean activated = m_activator.unknown_adapter(this, poa_name);
        if (!activated)
          throw new AdapterNonExistent(poa_name + " activation failed.");

        // Tha activator should add the child to the childrent table.
        for (int i = 0; i < children.size(); i++)
          {
            child = (POA) children.get(i);
            if (child.the_name().equals(poa_name))
              return child;
          }
        throw new AdapterNonExistent(poa_name + " not created. ");
      }
    else
      throw new AdapterNonExistent(poa_name);
  }

  /**
   * Generate the Object Id for the given servant and add the servant to the
   * Active Object Map using this Id a a key. If the servant activator is set,
   * its incarnate method will be called.
   * 
   * @param a_servant a servant that would serve the object with the returned
   * Object Id. If null is passed, under apporoprate policies the servant
   * activator is invoked.
   * 
   * @return the generated objert Id for the given servant.
   * 
   * @throws ServantAlreadyActive if this servant is already in the Active
   * Object Map and the UNIQUE_ID policy applies.
   * 
   * @throws WrongPolicy if the required policies SYSTEM_ID and RETAIN do not
   * apply to this POA.
   */
  public byte[] activate_object(Servant a_servant)
    throws ServantAlreadyActive, WrongPolicy
  {
    checkDiscarding();
    required(ServantRetentionPolicyValue.RETAIN);
    required(IdAssignmentPolicyValue.SYSTEM_ID);

    AOM.Obj exists = aom.findServant(a_servant);

    if (exists != null)
      {
        if (exists.isDeactiveted())
          {
            // If exists but deactivated, activate and return
            // the existing key.
            exists.setDeactivated(false);
            incarnate(exists, exists.key, a_servant, false);
            return exists.key;
          }
        else if (applies(IdUniquenessPolicyValue.UNIQUE_ID))
          throw new ServantAlreadyActive();

        // It multiple ids are allowed, exit block allowing repetetive
        // activations.
      }

    byte[] object_key = AOM.getFreeId();
    ServantDelegateImpl delegate = new ServantDelegateImpl(a_servant, this,
      object_key);
    create_and_connect(object_key,
      a_servant._all_interfaces(this, object_key)[0], delegate);
    return object_key;
  }

  /**
   * Add the given servant to the Active Object Map as a servant for the object
   * with the provided Object Id. If the servant activator is set, its incarnate
   * method will be called.
   * 
   * @param an_Object_Id an object id for the given object.
   * @param a_servant a servant that will serve the object with the given Object
   * Id. If null is passed, under apporoprate policies the servant activator is
   * invoked.
   * 
   * @throws ObjectAlreadyActive if the given object id is already in the Active
   * Object Map.
   * @throws ServantAlreadyActive if the UNIQUE_ID policy applies and this
   * servant is already in use.
   * @throws WrongPolicy if the required RETAIN policy does not apply to this
   * POA.
   * @throws BAD_PARAM if the passed object id is invalid due any reason.
   */
  public void activate_object_with_id(byte[] an_Object_Id, Servant a_servant)
                               throws ServantAlreadyActive, ObjectAlreadyActive,
                                      WrongPolicy
  {
    activate_object_with_id(an_Object_Id, a_servant, false);
  }

  /**
   * Same as activate_object_with_id, but permits gnuForwardRequest forwarding
   * exception. This is used when the activation is called from the remote
   * invocation context and we have possibility to return the forwarding
   * message.
   */
  public void activate_object_with_id(byte[] an_Object_Id, Servant a_servant,
    boolean use_forwarding)
    throws ServantAlreadyActive, ObjectAlreadyActive, WrongPolicy
  {
    checkDiscarding();
    required(ServantRetentionPolicyValue.RETAIN);

    // If the UNIQUE_ID applies, the servant being passed must not be
    // already active.
    if (applies(IdUniquenessPolicyValue.UNIQUE_ID))
      {
        AOM.Obj sx = aom.findServant(a_servant, false);
        if (sx != null)
          throw new ServantAlreadyActive();
      }

    AOM.Obj exists = aom.get(an_Object_Id);
    if (exists != null)
      {
        if (exists.servant == null)
          {
            locateServant(an_Object_Id, a_servant, exists, use_forwarding);
            exists.setDeactivated(false);
          }
        else if (exists.isDeactiveted())
          {
            exists.setDeactivated(false);
            incarnate(exists, an_Object_Id, a_servant, use_forwarding);
          }
        else
          throw new ObjectAlreadyActive();
      }
    else
      {
        ServantDelegateImpl delegate = new ServantDelegateImpl(a_servant, this,
          an_Object_Id);
        create_and_connect(an_Object_Id, a_servant._all_interfaces(this,
          an_Object_Id)[0], delegate);
      }
  }

  /**
   * Locate the servant for this object Id and connect it to ORB.
   * 
   * @param an_Object_Id the object id.
   * @param a_servant the servant (may be null).
   * @param exists an existing active object map entry.
   * @param use_forwarding allow to throw the gnuForwardRequest if the activator
   * throws ForwardRequest.
   * 
   * @throws OBJ_ADAPTER minor 4 if the servant cannot be located (the required
   * servant manager may be missing).
   */
  private void locateServant(byte[] an_Object_Id, Servant a_servant,
                             AOM.Obj exists, boolean use_forwarding
                            )
                      throws InternalError
  {
    // An object was created with create_reference.
    gnuServantObject object = (gnuServantObject) exists.object;
    if (servant_activator != null)
      {
        exists.setServant(incarnate(exists, an_Object_Id, a_servant,
                                    use_forwarding
                                   )
                         );
      }
    else if (default_servant != null)
      {
        exists.setServant(default_servant);
      }
    if (exists.servant == null)
      {
        exists.setServant(a_servant);
      }
    if (exists.servant == null)
      {
        throw new OBJ_ADAPTER("no servant", 4, CompletionStatus.COMPLETED_NO);
      }

    ServantDelegateImpl delegate =
      new ServantDelegateImpl(exists.servant, this, an_Object_Id);
    exists.servant._set_delegate(delegate);
    object.setServant(exists.servant);
    connect_to_orb(an_Object_Id, delegate.object);
  }

  /**
   * Deactivate object with the given id.
   *
   * The deactivated object will continue to process requests that arrived
   * before decativation. If this POA has the associated
   * servant manager, a {@link ServantActivatorOperations#etherealize} is
   * immediately invoked on the passed id.
   *
   * @throws WrongPolicy if the required RETAIN policy does not apply to
   * this POA.
   */
  public void deactivate_object(byte[] the_Object_Id)
                         throws ObjectNotActive, WrongPolicy
  {
    required(ServantRetentionPolicyValue.RETAIN);

    AOM.Obj exists = aom.get(the_Object_Id);

    if (exists == null || exists.isDeactiveted())
      throw new ObjectNotActive();

    exists.setDeactivated(true);

    // Check if this servant is serving something else.
    aom.remove(the_Object_Id);

    AOM.Obj other = aom.findServant(exists.servant, false);

    boolean remaining = other != null;

    aom.put(exists);

    if (servant_activator != null)
      servant_activator.etherealize(the_Object_Id, this, exists.servant, false,
                                    remaining
                                   );
  }

  /**
  * Create the object reference, encapsulating the given repository Id and
  * the Object Id, generated by this POA. The returned object will not be
  * activated by default and may be activated on the first invocation by
  * the servant manager (if it is set and if policies are applicable).
  *
  * @param a_repository_id the repository id for the given object, can
  * be null if to be requested from the servant later.
  *
  * @throws WrongPolicy if the required SYSTEM_ID policy does not apply to
  * this POA.
  */
  public org.omg.CORBA.Object create_reference(String a_repository_id)
                                        throws WrongPolicy
  {
    required(IdAssignmentPolicyValue.SYSTEM_ID);
    return create_reference_with_id(AOM.getFreeId(), a_repository_id);
  }

  /**
   * <p>
   * Create the object reference, encapsulating the given repository Id and
   * the given Object Id. The returned object will <i>not</i> be
   * activated by default and may be activated on the first invocation by
   * the servant manager (if the IMPLICIT_ACTIVATION policy applies).
   *
   * @param an_object_id the object id for the object being created. If this
   * POA uses the SYSTEM_ID policy, the portable application should only
   * pass the ids, generated by this POA.
   *
   * @param a_repository_id the repository id for the object being created,
   * can be null if this information should be later requested from the
   * servant.
   */
  public org.omg.CORBA.Object create_reference_with_id(byte[] an_object_id,
    String a_repository_id
   )
  {
    String[] ids;
    if (a_repository_id == null)
      ids = null;
    else
      ids = new String[] { a_repository_id };

    // Check maybe such object is already activated.
    AOM.Obj e = aom.get(an_object_id);

    Servant servant;
    if (e == null)
      {
        servant = null;
      }
    else
      {
        servant = e.servant;
        e.setDeactivated(false);
      }

    gnuServantObject object =
      new gnuServantObject(ids, an_object_id, this, m_orb);
    object._set_delegate(new LocalDelegate(object, this, an_object_id));
    aom.add(object.Id, object, servant, this);
    connect_to_orb(an_object_id, object);

    return object;
  }

  /**
   * Creates a new POA as a child of the target POA.
   *
   * @param child_name the name of the child POA being created.
   * @param manager the manager that will control the new POA. If this parameter
   * is null, a new POA manager is created and associated with the new POA.
   *
   * @param policies the policies, applicable for the parent POA. Policies
   * are <i>not</i> inherited from the parent POA.
   *
   * @return an newly created POA. The POA will be intially in the holding
   * state and must be activated to start processing requests.
   *
   * @throws AdapterAlreadyExists if the child with the given child_name
   * already exists for the current POA.
   * @throws InvalidPolicy if the policies conflict with each other or are
   * otherwise inappropriate.
   *
   * @see #the_children()
   */
  public POA create_POA(String child_name, POAManager manager, Policy[] policies)
                 throws AdapterAlreadyExists, InvalidPolicy
  {
    POA child;
    for (int i = 0; i < children.size(); i++)
      {
        child = (POA) children.get(i);
        if (child.the_name().equals(child_name))
          throw new AdapterAlreadyExists(name + "/" + child_name);
      }

    POA poa = createPoaInstance(child_name, manager, policies, m_orb);
    children.add(poa);
    return poa;
  }

  /**
   * Returns a default servant for this POA.
   *
   * @return a servant that will be used for requests for
   * which no servant is found in the Active Object Map.
   *
   * @throws NoServant if there is no default servant associated with this POA.
   * @throws WrongPolicy if the USE_DEFAULT_SERVANT policy is not active.
   */
  public Servant get_servant()
                      throws NoServant, WrongPolicy
  {
    required(RequestProcessingPolicyValue.USE_DEFAULT_SERVANT);
    if (default_servant == null)
      throw new NoServant();
    return default_servant;
  }

  /**
   * Sets the default servant for this POA.
   *
   * @param a_servant a servant that will be used for requests for
   * which no servant is found in the Active Object Map.
   *
   * @throws WrongPolicy if the USE_DEFAULT_SERVANT policy is not active.
   */
  public void set_servant(Servant a_servant)
                   throws WrongPolicy
  {
    required(RequestProcessingPolicyValue.USE_DEFAULT_SERVANT);
    default_servant = a_servant;
  }

  /**
   * Set a servant manager for this POA.
   *
   * @param a servant manager being set. If the RETAIN policy applies, the
   * manager must implement a {@link ServantActivator}. If the NON_RETAIN
   * policy applies, the manager must implement a {@link ServantLocator}.
   *
   * @throws WrongPolicy if the required USE_SERVANT_MANAGER policy does not
   * apply to this POA.
   *
   * @throws OBJ_ADAPTER minor code 4 if the passed manager does not
   * implement the required interface ({@link ServantActivator},
   * {@link ServantLocator}). The POA, that has the RETAIN policy uses
   * servant managers that are ServantActivators. When the POA has the
   * NON_RETAIN policy it uses servant managers that are ServantLoacators.
   *
   * @throws BAD_INV_ORDER minor code 6 if the method is called more than once
   * on the same POA. The manager can be set only once.
   */
  public void set_servant_manager(ServantManager a_manager)
                           throws WrongPolicy
  {
    required(RequestProcessingPolicyValue.USE_SERVANT_MANAGER);
    if (servant_activator != null || servant_locator != null)
      throw new BAD_INV_ORDER("Setting manager twice for " + name, 6,
                              CompletionStatus.COMPLETED_NO
                             );

    if (applies(ServantRetentionPolicyValue.RETAIN))
      {
        if (a_manager instanceof ServantActivator)
          servant_activator = (ServantActivator) a_manager;
        else
          throw new OBJ_ADAPTER("RETAIN requires ServantActivator", 4,
                                CompletionStatus.COMPLETED_NO
                               );
      }
    else if (applies(ServantRetentionPolicyValue.NON_RETAIN))
      {
        if (a_manager instanceof ServantLocator)
          servant_locator = (ServantLocator) a_manager;
        else
          throw new OBJ_ADAPTER("NON_RETAIN requires ServantLocator", 4,
                                CompletionStatus.COMPLETED_NO
                               );
      }
    else
      throw new WrongPolicy("No servant retention policy is specified.");
  }

  /**
   * Get the servant manager, associated with this POA.
   *
   * @return the associated servant manager or null if it has
   * been previously set.
   *
   * @throws WrongPolicy if the required USE_SERVANT_MANAGER policy does not
   * apply to this POA.
   */
  public ServantManager get_servant_manager()
                                     throws WrongPolicy
  {
    required(RequestProcessingPolicyValue.USE_SERVANT_MANAGER);

    if (servant_activator != null)
      return servant_activator;
    else
      return servant_locator;
  }

  /**
   * Get the unique Id of the POA in the process in which it is created.
   * This Id is needed by portable interceptors. The id is unique
   * for the life span of the POA in the process. For persistent
   * POAs, if a POA is created in the same path with the same name as
   * another POA, these POAs are identical have the same id. All transient
   * POAs are assumed unique.
   */
  public byte[] id()
  {
    if (m_poa_id != null)
      return m_poa_id;
    else
      {
        BufferedCdrOutput buffer = new BufferedCdrOutput();
        POA p = this;
        while (p != null)
          {
            buffer.write_string(p.the_name());
            p = p.the_parent();
          }
        m_poa_id = buffer.buffer.toByteArray();
        return m_poa_id;
      }
  }

  /**
   * Returns the reference to the active object with the given Id.
   *
   * @param the_Object_Id the object id.
   *
   * @throws ObjectNotActive if there is no active object with such Id
   * in the scope of this POA.
   * @throws WrongPolicy if the required RETAIN policy does not apply to
   * this POA.
   */
  public org.omg.CORBA.Object id_to_reference(byte[] the_Object_Id)
                                       throws ObjectNotActive, WrongPolicy
  {
    required(ServantRetentionPolicyValue.RETAIN);

    AOM.Obj ref = aom.get(the_Object_Id);
    if (ref == null)
      throw new ObjectNotActive();
    else
      return ref.object;
  }

  /**
   * Returns the servant that serves the active object with the given Id.
   *
   * @param the_Object_Id the object id.
   *
   * @throws ObjectNotActive if there is no active object with such Id or
   * it is not currently active.
   * @throws WrongPolicy. This method requires either RETAIN or
   * USE_DEFAULT_SERVANT policies and reaises the WrongPolicy if none of them
   * apply to this POA.
   */
  public Servant id_to_servant(byte[] the_Object_Id)
                        throws ObjectNotActive, WrongPolicy
  {
    if (applies(ServantRetentionPolicyValue.RETAIN))
      {
        AOM.Obj ref = aom.get(the_Object_Id);
        if (ref == null || ref.isDeactiveted())
          {
            if (default_servant != null)
              return default_servant;
            else
              throw new ObjectNotActive();
          }
        else if (ref.servant != null)
          return ref.servant;
        else if (default_servant != null)
          return default_servant;
        else
          throw new ObjectNotActive();
      }
    else if (default_servant != null)
      {
        return default_servant;
      }
    else
      throw new WrongPolicy("Either RETAIN or USE_DEFAULT_SERVANT required.");
  }

  /**
   * Returns the Object Id, encapsulated in the given object reference.
   *
   * @param the_Object the object that has been previously created with this
   * POA. It need not be active.
   *
   * @throws WrongAdapter if the passed object is not known for this POA.
   * @throws WrongPolicy never (declared for the future extensions only).
   */
  public byte[] reference_to_id(org.omg.CORBA.Object the_Object)
                         throws WrongAdapter, WrongPolicy
  {
    AOM.Obj ref = aom.findObject(the_Object);
    if (ref == null)
      throw new WrongAdapter();
    return ref.key;
  }

  /**
   * Returns the servant that is serving this object.
   *
   * @return if the RETAIN policy applies and the object is in the Active
   * Object Map, the method returns the servant, associated with this object.
   * Otherwise, if the USE_DEFAULT_SERVANT policy applies, the method returns
   * the default servant (if one was set).
   *
   * @throws ObjectNotActive if none of the conditions above are satisfied.
   * @throws WrongAdapter if the object reference was not created with this POA.
   * @throws WrongPolicy. This method requires either RETAIN or
   * USE_DEFAULT_SERVANT policies and reaises the WrongPolicy if none of them
   * apply to this POA.
   */
  public Servant reference_to_servant(org.omg.CORBA.Object the_Object)
                               throws ObjectNotActive, WrongPolicy,
                                      WrongAdapter
  {
    if (applies(ServantRetentionPolicyValue.RETAIN))
      {
        AOM.Obj ref = aom.findObject(the_Object);
        if (ref == null)
          throw new WrongAdapter();
        else if (ref.isDeactiveted() || ref.servant == null)
          {
            if (default_servant != null)
              return default_servant;
            else
              throw new ObjectNotActive();
          }
        else
          return ref.servant;
      }
    else if (default_servant != null)
      {
        return default_servant;
      }
    else
      throw new WrongPolicy("Either RETAIN or USE_DEFAULT_SERVANT required.");
  }

  /**
  * Returns the id of the object, served by the given servant
  * (assuming that the servant serves only one object).
  * The id is found in one of the following ways.
  * <ul>
  * <li>If the POA has both the RETAIN and the UNIQUE_ID policy and
  * the specified servant is active, the method return the Object Id associated
  * with that servant.
  * </li><li>
  * If the POA has both the RETAIN and the IMPLICIT_ACTIVATION policy and
  * either the POA has the MULTIPLE_ID policy or the specified servant is
  * inactive, the method activates the servant using a POA-generated Object Id
  * and the Interface Id associated with the servant, and returns that
  * Object Id.
  * </li>
  * <li>If the POA has the USE_DEFAULT_SERVANT policy, the servant specified
  * is the default servant, and the method is being invoked in the context of
  * executing a request on the default servant, the method returns the
  * ObjectId associated with the current invocation.
  * </li>
  * </ul>
  * @throws ServantNotActive in all cases, not listed in the list above.
  * @throws WrongPolicy The method requres USE_DEFAULT_SERVANT policy or
  * a combination of the RETAIN policy and either the UNIQUE_ID or
  * IMPLICIT_ACTIVATION policies and throws the WrongPolicy if these conditions
  * are not satisfied.
  */
  public byte[] servant_to_id(Servant the_Servant)
                       throws ServantNotActive, WrongPolicy
  {
    if (applies(RequestProcessingPolicyValue.USE_DEFAULT_SERVANT) ||
        applies(ServantRetentionPolicyValue.RETAIN) &&
        (
          applies(IdUniquenessPolicyValue.UNIQUE_ID) ||
          applies(ImplicitActivationPolicyValue.IMPLICIT_ACTIVATION)
        )
       )
      {
        AOM.Obj ref = null;
        if (!applies(IdUniquenessPolicyValue.MULTIPLE_ID))
          ref = aom.findServant(the_Servant);
        if (ref == null &&
            applies(ImplicitActivationPolicyValue.IMPLICIT_ACTIVATION)
           )
          {
            // Try to activate.
            try
              {
                return activate_object(the_Servant);
              }
            catch (ServantAlreadyActive ex)
              {
                // Either it shuld not be or the policy allows multiple ids.
                throw new InternalError();
              }
          }
        if (ref == null)
          throw new ServantNotActive();
        else
          return ref.key;
      }
    else
      throw new WrongPolicy("(RETAIN and UNIQUE ID) " +
                            "or USE_DEFAULT_SERVANT required."
                           );
  }

  /**
   * <p>
   * Converts the given servant to the object reference. The servant will serve
   * all methods, invoked on the returned object. The returned object reference
   * can be passed to the remote client, enabling remote invocations.
   * </p>
   * <p>
   * If the specified servant is active, it is returned. Otherwise, if the POA
   * has the IMPLICIT_ACTIVATION policy the method activates the servant. In
   * this case, if the servant activator is set, the
   * {@link ServantActivatorOperations#incarnate} method will be called.
   * </p>
   * 
   * @throws ServantNotActive if the servant is inactive and no
   * IMPLICIT_ACTIVATION policy applies.
   * @throws WrongPolicy This method needs the RETAIN policy and either the
   * UNIQUE_ID or IMPLICIT_ACTIVATION policies.
   * 
   * @return the object, exposing the given servant in the context of this POA.
   */
  public org.omg.CORBA.Object servant_to_reference(Servant the_Servant)
    throws ServantNotActive, WrongPolicy
  {
    required(ServantRetentionPolicyValue.RETAIN);

    AOM.Obj exists = null;

    if (!applies(IdUniquenessPolicyValue.MULTIPLE_ID))
      exists = aom.findServant(the_Servant);

    if (exists != null)
      {
        if (exists.isDeactiveted())
          {
            if (applies(ImplicitActivationPolicyValue.IMPLICIT_ACTIVATION))
              {
                checkDiscarding();
                exists.setDeactivated(false);
                incarnate(exists, exists.key, the_Servant, false);
              }
            else
              throw new ServantNotActive();
          }
        else
          return exists.object;
      }
    if (exists == null
      && applies(ImplicitActivationPolicyValue.IMPLICIT_ACTIVATION))
      {
        checkDiscarding();

        byte[] object_key = AOM.getFreeId();

        ServantDelegateImpl delegate = new ServantDelegateImpl(the_Servant,
          this, object_key);
        create_and_connect(object_key, the_Servant._all_interfaces(this,
          object_key)[0], delegate);

        return delegate.object;
      }
    else
      throw new ServantNotActive();
  }

  /**
   * Incarnate in cases when request forwarding is not expected because the
   * servant must be provided by the servant activator.
   * 
   * @param x the aom entry, where the object is replaced by value, returned by
   * servant activator (if not null).
   * 
   * @param key the object key.
   * 
   * @param a_servant the servant that was passed as a parameter in the
   * activation method.
   * 
   * @param use_forwarding if true, the gnuForwardRequest is throw under the
   * forwarding exception (for remote client). Otherwise, the request is
   * internally redirected (for local invocation).
   */
  private Servant incarnate(AOM.Obj x, byte[] object_key,
                            Servant a_servant, boolean use_forwarding
                           )
  {
    if (servant_activator != null)
      {
        Servant servant;
        try
          {
            servant = servant_activator.incarnate(object_key, this);
          }
        catch (ForwardRequest ex)
          {
            if (use_forwarding)
              throw new gnuForwardRequest(ex.forward_reference);
            else
              servant =
                ForwardedServant.create((ObjectImpl) ex.forward_reference);
          }
        if (servant != null && x != null)
          x.setServant(servant);
        if (servant == null && x != null)
          servant = x.servant;
        return servant;
      }
    else if (a_servant != null)
      {
        x.setServant(a_servant);
        return a_servant;
      }
    else if (x.servant != null)
      {
        return x.servant;
      }
    else if (default_servant != null)
      {
        x.setServant(default_servant);
        return x.servant;
      }
    else
      throw new BAD_INV_ORDER("No servant given and the servant activator not set");
  }

  /**
   * Return the POA manager, associated with this POA.
   *
   * @return the associated POA manager (always available).
   */
  public POAManager the_POAManager()
  {
    return m_manager;
  }

  /**
   * Returns the adapter activator, associated with this POA.
   * The newly created POA has no activator (null would be
   * returned). The ORB root POA also initially has no activator.
   *
   * @return tha adapter activator or null if this POA has no
   * associated adapter activator.
   */
  public AdapterActivator the_activator()
  {
    return m_activator;
  }

  /**
  * Set the adapter activator for this POA.
  *
  * @param the activator being set.
  */
  public void the_activator(AdapterActivator an_activator)
  {
    m_activator = an_activator;
  }

  /**
  * The children of this POA.
  *
  * @return the array of all childs for this POA.
  */
  public POA[] the_children()
  {
    POA[] poas = new POA[ children.size() ];
    for (int i = 0; i < poas.length; i++)
      {
        poas [ i ] = (POA) children.get(i);
      }
    return poas;
  }

  /**
   * Return the name of this POA.
   *
   * @return the name of POA, relative to its parent.
   */
  public String the_name()
  {
    return name;
  }
  ;

  /**
   * Return the parent of this POA.
   *
   * @return the parent POA or <code>null</code> if this is a root POA.
   */
  public POA the_parent()
  {
    return parent;
  }

  /** {@inheritDoc} */
  public IdAssignmentPolicy create_id_assignment_policy(IdAssignmentPolicyValue a_value)
  {
    return new gnuIdAssignmentPolicy(a_value);
  }

  /** {@inheritDoc} */
  public IdUniquenessPolicy create_id_uniqueness_policy(IdUniquenessPolicyValue a_value)
  {
    return new gnuIdUniquenessPolicy(a_value);
  }

  /** {@inheritDoc} */
  public ImplicitActivationPolicy create_implicit_activation_policy(ImplicitActivationPolicyValue a_value)
  {
    return new gnuImplicitActivationPolicy(a_value);
  }

  /** {@inheritDoc} */
  public LifespanPolicy create_lifespan_policy(LifespanPolicyValue a_value)
  {
    return new gnuLifespanPolicy(a_value);
  }

  /** {@inheritDoc} */
  public RequestProcessingPolicy create_request_processing_policy(RequestProcessingPolicyValue a_value)
  {
    return new gnuRequestProcessingPolicy(a_value);
  }

  /** {@inheritDoc} */
  public ServantRetentionPolicy create_servant_retention_policy(ServantRetentionPolicyValue a_value)
  {
    return new gnuServantRetentionPolicy(a_value);
  }

  /** {@inheritDoc} */
  public ThreadPolicy create_thread_policy(ThreadPolicyValue a_value)
  {
    return new gnuThreadPolicy(a_value);
  }

  /**
   * <p>
   * Destroy this POA and all descendant POAs. The destroyed POAs can be later
   * re-created via {@link AdapterActivator} or by invoking {@link #create_POA}.
   * This differs from {@link PoaManagerOperations#deactivate} that does not
   * allow recreation of the deactivated POAs. After deactivation, recreation is
   * only possible if the POAs were later destroyed.
   * </p>
   * <p>
   * The remote invocation on the target, belonging to the POA that is currently
   * destroyed return the remote exception ({@link TRANSIENT}, minor code 4).
   * </p>
   * 
   * @param etherealize_objects if true, and POA has RETAIN policy, and the
   * servant manager is available, the servant manager method
   * {@link ServantActivatorOperations#etherealize} is called for each <i>active</i>
   * object in the Active Object Map. This method should not try to access POA
   * being destroyed. If <code>destroy</code> is called multiple times before
   * the destruction completes, the etherialization should be invoked only once.
   * 
   * @param wait_for_completion if true, the method waits till the POA being
   * destroyed completes all current requests and etherialization. If false, the
   * method returns immediately.
   */
  public void destroy(boolean etherealize_objects, boolean wait_for_completion)
  {
    // Notify the IOR interceptors about that the POA is destroyed.
    if (m_orb.iIor != null)
      m_orb.iIor.adapter_state_changed(
        new ObjectReferenceTemplate[] { getReferenceTemplate() },
        NON_EXISTENT.value);

    if (wait_for_completion)
      waitWhileRunning();

    // Nofify the IOR interceptors that the POA is destroyed.
    if (m_manager instanceof gnuPOAManager)
      {
        ((gnuPOAManager) m_manager).poaDestroyed(this);
      }

    // Put the brake instead of manager, preventing the subsequent
    // requests.
    gnuPOAManager g = new gnuPOAManager();
    g.state = State.INACTIVE;
    m_manager = g;

    // Disconnect from parent.
    if (parent instanceof gnuPOA)
      {
        ((gnuPOA) parent).children.remove(this);
      }

    unregisterFromManager();

    // Disconnect from the ORB all objects, registered with this POA.
    ArrayList keys = new ArrayList();
    keys.addAll(aom.keySet());

    byte[] key;
    AOM.Obj obj;
    for (int i = 0; i < keys.size(); i++)
      {
        key = (byte[]) keys.get(i);
        obj = aom.get(key);
        if (obj.poa == this)
          m_orb.disconnect(obj.object);
      }

    m_orb.identityDestroyed(this);

    if (etherealize_objects && servant_activator != null && !m_inDestruction)
      {
        etherealizeAll();
      }
    m_inDestruction = true;

    POA[] ch = the_children();
    for (int i = 0; i < ch.length; i++)
      {
        ch[i].destroy(etherealize_objects, wait_for_completion);
      }
  }

  /**
   * Destroy this POA if it has not been destroyed, destroys it.
   */
  protected void finalize()
                   throws java.lang.Throwable
  {
    if (!m_inDestruction)
      destroy(false, false);
  }

  /**
   * Remove self from the manager list.
   */
  private void unregisterFromManager()
  {
    if (m_manager instanceof gnuPOAManager)
      {
        gnuPOAManager p = (gnuPOAManager) m_manager;
        p.removePOA(this);
      }
  }

  /**
   * Get the policy of the given type, associated with this POA.
   *
   * @param a_policy_type a type of the requested policy.
   * @return a policy of the given type, applyting to this POA.
   *
   * @throws org.omg.CORBA.BAD_PARAM if the policy of this type has not
   * been specified for this POA.
   */
  public Policy _get_policy(int a_policy_type)
                     throws org.omg.CORBA.BAD_PARAM
  {
    for (int i = 0; i < s_policies.length; i++)
      {
        if (s_policies [ i ].policy_type() == a_policy_type)
          return s_policies [ i ].copy();
      }
    throw new BAD_PARAM("No policy type " + a_policy_type);
  }

  /**
   * Get the copy of the policy array.
   */
  public Policy[] getPolicyArray()
  {
    Policy[] r = new Policy[ s_policies.length ];
    for (int i = 0; i < s_policies.length; i++)
      {
        r [ i ] = s_policies [ i ].copy();
      }
    return r;
  }

  /**
   * The POAs cannot be created by this method.
   *
   * @specnote this is also not possible in Suns jdk at least till 1.4.
   *
   * @throws NO_IMPLEMENT always.
   */
  public org.omg.CORBA.Object _set_policy_override(Policy[] policies,
                                                   SetOverrideType how
                                                  )
  {
    throw new NO_IMPLEMENT("Use createPOA instead.");
  }

  /**
   * Get the ORB, where this POA is connected.
   */
  public ORB orb()
  {
    return m_orb;
  }

  /**
   * Connect the given delegate under the given key, also calling incarnate.
   */
  private void create_and_connect(byte[] object_key, String repository_id,
    ServantDelegateImpl delegate)
  {
    aom.add(delegate);
    connect_to_orb(object_key, getReferenceFactory().make_object(repository_id,
      object_key));
    if (servant_activator != null)
      incarnate(null, object_key, delegate.servant, false);
  }

  /**
   * Check if the POA is not in a discarding mode. The activation
   * operations are forbidded in discarding mode.
   *
   * @throws TRANSIENT if the POA is in discarding mode.
   */
  private void checkDiscarding()
                        throws TRANSIENT
  {
    if (m_manager.get_state() == State.DISCARDING)
      throw new TRANSIENT("Discarding mode", 1, CompletionStatus.COMPLETED_MAYBE);
  }

  /**
   * Connect the given delegate object to orb.
   */
  protected void connect_to_orb(byte[] an_Object_Id, org.omg.CORBA.Object object)
  {
    if (applies(ThreadPolicyValue.SINGLE_THREAD_MODEL))
      m_orb.connect_1_thread(object, toIORKey(an_Object_Id), this);
    else
      m_orb.connect(object, toIORKey(an_Object_Id));
  }

  /**
   * Returns the representation of this POA tree.
   */
  public String toString()
  {
    StringBuffer b = new StringBuffer(name);

    if (children.size() != 0)
      {
        b.append(" (");

        for (int i = 0; i < children.size(); i++)
          {
            b.append(children.get(i));
            if (i < children.size() - 2)
              b.append(", ");
          }
        b.append(")");
      }
    return b.toString();
  }

  /**
   * Check if the policy set is valid.
   */
  protected boolean validatePolicies(Policy[] a)
                              throws InvalidPolicy
  {
    if (applies(ServantRetentionPolicyValue.NON_RETAIN))
      {
        if (!applies(RequestProcessingPolicyValue.USE_DEFAULT_SERVANT) &&
            !applies(RequestProcessingPolicyValue.USE_SERVANT_MANAGER)
           )
          {
            short p = 0;
            for (short i = 0; i < a.length; i++)
              {
                if (a [ i ].policy_type() == SERVANT_RETENTION_POLICY_ID.value)
                  p = i;
              }
            throw new InvalidPolicy("NON_RETAIN requires either " +
                                    "USE_DEFAULT_SERVANT or USE_SERVANT_MANAGER",
                                    p
                                   );
          }
      }
    return true;
  }

  /**
   * Recursively searches for the given object in the POA tree.
   */
  public AOM.Obj findObject(org.omg.CORBA.Object object)
  {
    AOM.Obj h = aom.findObject(object);
    if (h != null)
      return h;
    else
      {
        for (int i = 0; i < children.size(); i++)
          {
            h = ((gnuPOA) children.get(i)).findObject(object);
            if (h != null)
              return h;
          }
      }
    return h;
  }
  
  /**
   * Recursively searches for the given key in the POA tree.
   * @param ior_key the key, ecapsulating both object
   * and poa ids.
   * @return
   */
  public AOM.Obj findKey(byte[] object_id, byte[] poa_id)
  {
    AOM.Obj h = null;
    if (Arrays.equals(poa_id, id()))
      h = aom.get(object_id);
    if (h != null)
      return h;
    else
      {
        for (int i = 0; i < children.size(); i++)
          {
            h = ((gnuPOA) children.get(i)).findKey(object_id, poa_id);
            if (h != null)
              return h;
          }
      }
    return h;
  }

  /**
   * Parses the given key, extracts poa and object id and searches
   * for such reference.
   */
  public AOM.Obj findIorKey(byte[] ior_key)
  {
    BufferredCdrInput in = new BufferredCdrInput(ior_key);
    int signature = in.read_long();
    if (signature != SIGNATURE)
      return null;

    byte[] id = in.read_sequence();
    byte[] poa = in.read_sequence();
    return findKey(id, poa);
  }

  /**
   * Converts the object Id into the IOR key. IOR key must be
   * unique in the scope of the ORB, and Ids only in the scope of POA.
   * Hence the IOR key includes the POA identifiers.
   */
  public byte[] toIORKey(byte[] object_id)
  {
    BufferedCdrOutput buffer = new BufferedCdrOutput();
    buffer.write_long(SIGNATURE);
    buffer.write_sequence(object_id);
    buffer.write_sequence(id());
    return buffer.buffer.toByteArray();
  }

  /**
   * Extracts the object id from the ior key.
   *
   * @param ior_key
   *
   * @return the encapsulated object ior key or null if
   * this ior key either refers a different POA or encoding signature
   * mismatch.
   */
  public byte[] idFormIor(byte[] ior_key)
  {
    BufferredCdrInput in = new BufferredCdrInput(ior_key);
    int signature = in.read_long();
    if (signature != SIGNATURE)
      return null;

    byte[] object_id = in.read_sequence();
    byte[] poa_id = in.read_sequence();
    if (Arrays.equals(poa_id, id()))
      return object_id;
    else
      return null;
  }
  
  /**
   * Recursively searches for the given servant in the POA tree.
   */
  public AOM.Obj findServant(Servant servant)
  {
    AOM.Obj h = aom.findServant(servant);
    if (h != null)
      return h;
    else
      {
        for (int i = 0; i < children.size(); i++)
          {
            h = ((gnuPOA) children.get(i)).findServant(servant);
            if (h != null)
              return h;
          }
      }
    return h;
  }
  
  /**
   * Get the object reference template of this POA.
   * Instantiate a singleton instance, if required.
   */
  public ObjectReferenceTemplate getReferenceTemplate()
  {
    if (refTemplate == null)
      refTemplate = new RefTemplate();
    
    return refTemplate;
  }
  
  public ObjectReferenceFactory getReferenceFactory()
  {
    return m_object_factory;
  }
  
  public void setReferenceFactory(ObjectReferenceFactory factory)
  {
    m_object_factory = factory;
  }

  /**
   * Create the object (needed by the factory interface).
   */
  public Object make_object(String a_repository_id, byte[] an_object_id)
  {
    AOM.Obj existing = aom.get(an_object_id);
    // The object may already exist. In this case, it is just returned.
    if (existing != null && existing.object != null)
      return existing.object;
    else
      {
        return new gnuServantObject(new String[] { a_repository_id },
          an_object_id, this, m_orb);
      }
  }

  /**
   * Required by object reference factory ops.
   */
  public String[] _truncatable_ids()
  {
    return ref_template_ids;
  }
}